home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume10 / agetty2 / agetty.c < prev    next >
C/C++ Source or Header  |  1990-01-28  |  23KB  |  826 lines

  1. /*++
  2. /* NAME
  3. /*    agetty 8
  4. /* SUMMARY
  5. /*    alternative System-V getty for dial-up lines
  6. /* SYNOPSIS
  7. /*    agetty [-a alternate_rates] [-h] [-i] [-m] [-t timeout] port baud_rate
  8. /* DESCRIPTION
  9. /*    \fIagetty\fR opens a tty port, prompts for a login name and invokes the
  10. /*    /bin/login command. It is normally invoked by \fIinit(8)\fR.
  11. /*
  12. /*    \fIagetty\fR has some useful features for dial-up lines that are
  13. /*    not present in the System V Release 2 getty command:
  14. /* .IP o
  15. /*    Adapts the tty settings to parity bits and to
  16. /*    erase, kill and end-of-line characters found in its input. The
  17. /*    program understands 7-bit characters with even, odd, none or space
  18. /*    parity, and 8-bit characters with no parity. The following special
  19. /*    characters are recognized: @ and Control-U (kill); #, DEL and
  20. /*    back space (erase); carriage return and line feed (end of line).
  21. /* .IP o
  22. /*    Optionally recognizes the baud rate of incoming calls from the 
  23. /*    status messages produced by some multi-speed Hayes-compatible modems.
  24. /* .IP o
  25. /*    Optionally does not display the contents of the \fI/etc/issue\fR file.
  26. /* .PP
  27. /*    This program does not use the \fI/etc/gettydefs\fR file. Except for 
  28. /*    differences described in the documentation, the program appears to 
  29. /*    operate similar to the System-V Release 2 \fIgetty\fR program.
  30. /*
  31. /*    Options:
  32. /* .TP
  33. /* -a alternate_rates
  34. /*    Initially the program will use the \fIbaud_rate\fR as specified.
  35. /*    Upon receipt of successive BREAK characters the program will step
  36. /*    through the \fIalternate_rates\fR, which should be specified as a
  37. /*    comma-separated list (preferably in decreasing order). After all
  38. /*    \fIalternate_rates\fR have been tried, \fIagetty\fR will try the
  39. /*    speed specified with the \fIbaud_rate\fR argument and so on.
  40. /* .TP
  41. /* -h
  42. /*    Do not hang up the line. Normally, \fIagetty\fR will lower
  43. /*    DTR for two seconds to force a modem to hang up (if the hangup
  44. /*    feature has been compiled into the program).
  45. /* .TP
  46. /* -i
  47. /*    Do not display the contents of \fI/etc/issue\fR before writing the 
  48. /*    login prompt. Terminals or computer programs may become confused
  49. /*    when receiving lots of text at the wrong baud rate; dial-up scripts
  50. /*    may fail if the login prompt is preceded by too much text.
  51. /* .TP
  52. /* -m
  53. /*    Try to extract the baud rate of incoming calls from the status message
  54. /*    produced by some multi-speed Hayes-compatible modems. These usually
  55. /*    produce a status message of the form: "<junk><speed><junk>".
  56. /*    If no \fIspeed\fR is found within one second, the \fIbaud_rate\fR as
  57. /*    specified on the command line will be used. Since the \fI-m\fR feature 
  58. /*    will work only on lightly-loaded systems, you will probably want to use
  59. /*    it in combination with the \fI-a\fR option.
  60. /* .TP
  61. /* -t timeout
  62. /*    Causes the program to terminate if no user name could be read
  63. /*    within \fItimeout\fR seconds. This is useful only for dial-in lines.
  64. /* EXAMPLES
  65. /*    For hard-wired lines:
  66. /* .ti +5
  67. /*        /etc/agetty ttyM0 9600
  68. /*
  69. /*    For dial-in lines with a 300/1200/2400 baud multi-speed modem:
  70. /* .ti +5
  71. /*        /etc/agetty -t60 -m -a1200,300 ttyM1 2400
  72. /* FILES
  73. /*    /etc/utmp, the system log file.
  74. /*    /etc/issue, printed before the login prompt.
  75. /*    /dev/console, problem reports.
  76. /* BUGS
  77. /*    The baud-rate detection code (the \fI-m\fR option) only works if
  78. /*    \fIagetty\fR is scheduled soon enough after completion of a dial-in
  79. /*    call (within 30 ms with modems that talk at 2400 baud). For robustness,
  80. /*    always use the \fI-m\fR option in combination with the \fI-a\fR option.
  81. /*
  82. /*    The contents of the /etc/issue file and the login prompt are always
  83. /*    output with space parity.
  84. /* DIAGNOSTICS
  85. /*    All diagnostics are written to the console device. Error messages are
  86. /*    produced if the \fIport\fR argument does not specify a terminal; if 
  87. /*    there is no /etc/utmp entry for the current process; and so on.
  88. /* AUTHOR(S)
  89. /*    W.Z. Venema <wietse@wzv.win.tue.nl>
  90. /*    Eindhoven University of Technology
  91. /*    Department of Mathematics and Computer Science
  92. /*    Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  93. /* CREATION DATE
  94. /*    Sat Nov 25 22:51:05 MET 1989
  95. /* LAST MODIFICATION
  96. /*    90/01/28 17:53:06
  97. /* VERSION/RELEASE
  98. /*    1.26
  99. /*--*/
  100.  
  101. #ifndef lint
  102. static char sccsid[] = "@(#) agetty.c 1.26 1/28/90 17:53:06";
  103. #endif
  104.  
  105. #include <termio.h>
  106. #include <signal.h>
  107. #include <sys/types.h>
  108. #include <sys/stat.h>
  109. #include <varargs.h>
  110. #include <ctype.h>
  111. #include <utmp.h>
  112.  
  113.  /*
  114.   * Things you may want to modify.
  115.   * 
  116.   * HANGUP should be defined only if your tty driver is not able to hang up the
  117.   * modem (by briefly dropping DTR). If HANGUP is defined you probably cannot
  118.   * use the auto-baud and time-out features.
  119.   * 
  120.   * If ISSUE is not defined, agetty will never display the contents of the
  121.   * /etc/issue file. You will not want to spit out large "issue" files at the
  122.   * wrong baud rate.
  123.   * 
  124.   * You may disagree with the default line-editing etc. characters defined
  125.   * below. Note, however, that DEL cannot be used for interrupt generation
  126.   * and for line editing at the same time.
  127.   */
  128.  
  129. #define    ISSUE "/etc/issue"        /* shown before login prompt */
  130.  
  131. #define LOGIN "login: "            /* login prompt */
  132.  
  133. /* #define HANGUP            /* enable hangup code */
  134.  
  135. /* Some shorthands for control characters */
  136.  
  137. #define CTL(x)        (x ^ 0100)    /* Assumes ASCII dialect */
  138. #define    CR        CTL('M')    /* carriage return */
  139. #define    NL        CTL('J')    /* line feed */
  140. #define    BS        CTL('H')    /* back space */
  141. #define    DEL        CTL('?')    /* delete */
  142.  
  143. /* Defaults for line-editing etc. characters; you may want to change this */
  144.  
  145. #define DEF_INTR    CTL('C')    /* default interrupt character */
  146. #define DEF_QUIT    CTL('\\')    /* default quit char */
  147. #define DEF_KILL    CTL('U')    /* default kill char */
  148. #define DEF_EOF        CTL('D')    /* default EOF char */
  149. #define DEF_SWITCH    CTL('^')    /* default switch char */
  150. #define DEF_ERASE    BS        /* default erase char, see below */
  151. #define DEF_EOL        0
  152.  
  153.  /*
  154.   * This program does not need the standard-i/o library.  This keeps the
  155.   * executable small; useful for systems that do not have shared libaries
  156.   * (Sys-V Rel <3).
  157.   */
  158.  
  159. #define    BUFSIZ    1024
  160.  
  161. /* Storage for command-line options */
  162.  
  163. #define    MAXSPEED    10
  164.  
  165. struct options {
  166.     int     flags;            /* toggle switches, see below */
  167.     int     timeout;            /* time-out period */
  168.     int     numspeed;            /* number of baud rates to try */
  169.     int     curspeed;            /* current speed */
  170.     int     speeds[MAXSPEED];        /* baud rates to be tried */
  171.     char   *tty;            /* name of tty */
  172. };
  173.  
  174. #define    F_PARSE        (1<<0)        /* process modem status messages */
  175. #define    F_HANGUP    (1<<1)        /* hangup line */
  176. #define    F_ISSUE        (1<<2)        /* display /etc/issue */
  177.  
  178. /* Storage for things detected while the login name was read */
  179.  
  180. struct chardata {
  181.     int     erase;            /* erase character */
  182.     int     kill;            /* kill character */
  183.     int     eol;            /* end-of-line character */
  184.     int     parity;            /* what parity did we see */
  185.     int     capslock;            /* upper case without lower case */
  186. };
  187.  
  188. /* The following is used for understandable diagnostics */
  189.  
  190. extern int errno;
  191. extern char *sys_errlist[];
  192. static char *progname;
  193. extern char *strcpy();
  194. extern char *strcat();
  195.  
  196. /* ... */
  197.  
  198. main(argc, argv)
  199. int     argc;
  200. char  **argv;
  201. {
  202.     char   *logname;            /* login name, given to /bin/login */
  203.     char   *get_logname();
  204.     struct chardata chardata;        /* set by get_logname() */
  205.     struct termio termio;        /* terminal mode bits */
  206.     static struct options options = {
  207.     F_HANGUP | F_ISSUE,        /* hangup line and show /etc/issue */
  208.     0,                /* no timeout */
  209.     1,                /* no alternate baud rates */
  210.     0,                /* no alternate baud rates */
  211.     };
  212.  
  213.     progname = argv[0];
  214.  
  215.     /* Parse command-line arguments */
  216.  
  217.     parse_args(argc, argv, &options);
  218.  
  219.     /* Update the utmp file */
  220.  
  221.     update_utmp(options.tty);
  222.  
  223.     /* Open the tty as standard { input, output, error } */
  224.  
  225.     open_tty(options.tty, &termio);
  226.  
  227.     /* Optionally hang up the tty */
  228.  
  229.     if (options.flags & F_HANGUP)
  230.     hangup_tty(&termio);
  231.  
  232.     /* Initialize the termio settings (raw mode, eight-bit, blocking i/o) */
  233.  
  234.     termio_init(&termio, options.speeds[0]);
  235.  
  236.     /* Optionally detect the baud rate from the modem status message */
  237.  
  238.     if (options.flags & F_PARSE)
  239.     auto_baud(&termio);
  240.  
  241.     /* With dial-in lines, briefly pause to allow modems etc. to settle */
  242.  
  243.     if (options.timeout)
  244.     (void) sleep(1);
  245.  
  246.     /* Optional time-out feature */
  247.  
  248.     if (options.timeout)
  249.     (void) alarm((unsigned) options.timeout);
  250.  
  251.     /* Read the login name */
  252.  
  253.     while ((logname = get_logname(&options, &chardata, &termio)) == 0)
  254.     next_speed(&termio, &options);
  255.  
  256.     /* Disable time-out feature */
  257.  
  258.     if (options.timeout)
  259.     (void) alarm(0);
  260.  
  261.     /* Finalize the termio settings */
  262.  
  263.     termio_final(&termio, &chardata);
  264.  
  265.     /* Now the newline character should be properly written */
  266.  
  267.     (void) write(1, "\n", 1);
  268.  
  269.     /* Let /bin/login take care of password validation */
  270.  
  271.     (void) execl("/bin/login", "login", logname, (char *) 0);
  272.     error("%s: can't exec /bin/login", options.tty);
  273.     /* NOTREACHED */
  274. }
  275.  
  276. /* parse-args - parse command-line arguments */
  277.  
  278. parse_args(argc, argv, op)
  279. int     argc;
  280. char  **argv;
  281. struct options *op;
  282. {
  283.     extern char *optarg;        /* getopt */
  284.     extern int optind;            /* getopt */
  285.     int     c;
  286.  
  287.     while (isascii(c = getopt(argc, argv, "a:himt:"))) {
  288.     switch (c) {
  289.     case 'a':                /* enable auto-baud feature */
  290.         parse_speeds(op, optarg);
  291.         break;
  292.     case 'h':                /* do not hangup the tty */
  293.         op->flags &= ~F_HANGUP;
  294.         break;
  295.     case 'i':                /* do not show /etc/issue */
  296.         op->flags &= ~F_ISSUE;
  297.         break;
  298.     case 'm':                /* parse modem status message */
  299.         op->flags |= F_PARSE;
  300.         break;
  301.     case 't':                /* time out */
  302.         if ((op->timeout = atoi(optarg)) <= 0)
  303.         error("bad timeout value: %s", optarg);
  304.         break;
  305.     case '?':
  306.         usage();
  307.     }
  308.     }
  309.     if (argc != optind + 2)            /* check parameter count */
  310.     usage();
  311.     op->tty = argv[optind++];            /* tty name */
  312.     if ((op->speeds[0] = bcode(argv[optind])) <= 0)    /* baud rate */
  313.     error("bad speed: %s", argv[optind]);
  314. }
  315.  
  316. /* parse_speeds - parse alternate baud rates */
  317.  
  318. parse_speeds(op, arg)
  319. struct options *op;
  320. char   *arg;
  321. {
  322.     char   *strtok();
  323.     char   *cp;
  324.  
  325.     for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
  326.     if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
  327.         error("bad speed: %s", cp);
  328.     if (op->numspeed > MAXSPEED)
  329.         error("too many alternate speeds");
  330.     }
  331. }
  332.  
  333. /* update_utmp - update our utmp entry */
  334.  
  335. update_utmp(line)
  336. char   *line;
  337. {
  338.     struct utmp ut;
  339.     long    ut_size = sizeof(ut);    /* avoid nonsense */
  340.     int     ut_fd;
  341.     int     mypid = getpid();
  342.     long    time();
  343.     long    lseek();
  344.     char   *strncpy();
  345.  
  346.     /*
  347.      * The utmp file holds miscellaneous information about things started by
  348.      * /etc/init and other system-related events. Our purpose is to update
  349.      * the utmp entry for the current process, in particular the process type
  350.      * and the tty line we are listening to. Return successfully only if the
  351.      * utmp file can be opened for update, and if we are able to find our
  352.      * entry in the utmp file.
  353.      */
  354.  
  355.     if ((ut_fd = open(UTMP_FILE, 2)) < 0) {
  356.     error("%s: open for update", UTMP_FILE);
  357.     } else {
  358.     while (read(ut_fd, (char *) &ut, sizeof(ut)) == sizeof(ut)) {
  359.         if (ut.ut_type == INIT_PROCESS && ut.ut_pid == mypid) {
  360.         ut.ut_type = LOGIN_PROCESS;
  361.         ut.ut_time = time((long *) 0);
  362.         (void) strncpy(ut.ut_name, "LOGIN", sizeof(ut.ut_name));
  363.         (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
  364.         (void) lseek(ut_fd, -ut_size, 1);
  365.         (void) write(ut_fd, (char *) &ut, sizeof(ut));
  366.         (void) close(ut_fd);
  367.         return;
  368.         }
  369.     }
  370.     error("no utmp entry found for process id %u", mypid);
  371.     }
  372. }
  373.  
  374. /* open_tty - open tty as standard { input, output, error } */
  375.  
  376. open_tty(tty, tp)
  377. char   *tty;
  378. struct termio *tp;
  379. {
  380.     struct stat st;
  381.  
  382.     /* Close standard { input, output, error } files, just in case */
  383.  
  384.     (void) close(0);
  385.     (void) close(1);
  386.     (void) close(2);
  387.     errno = 0;                    /* ignore above errors */
  388.  
  389.     /* Make sure we are given a character device */
  390.  
  391.     if (chdir("/dev"))
  392.     error("/dev: chdir() failed");
  393.     if (stat(tty, &st) < 0)
  394.     error("/dev/%s: stat() failed", tty);
  395.     if ((st.st_mode & S_IFMT) != S_IFCHR)
  396.     error("not a character device: /dev/%s", tty);
  397.  
  398.     /* Set up new standard input, output and error files */
  399.  
  400.     if (open(tty, 2) != 0)            /* set up std input */
  401.     error("/dev/%s: cannot open as standard input", tty);
  402.     if (dup(0) != 1 || dup(0) != 2)        /* set up std out and std err */
  403.     error("%s: dup problem", tty);        /* we have a problem */
  404.     if (ioctl(0, TCGETA, tp) < 0)        /* read tty status bits */
  405.     error("%s: ioctl failed", tty);        /* this is not a terminal */
  406.  
  407.     /* It seems to be a terminal; set proper protections and ownership */
  408.  
  409.     (void) chown(tty, 0, 0);            /* root, sys */
  410.     (void) chmod(tty, 0622);            /* crw--w--w- */
  411.     errno = 0;                    /* ignore above errors */
  412. }
  413.  
  414. #ifdef lint
  415. #define    HANGUP
  416. #endif
  417.  
  418. /* hangup_tty - hang up by forcing DTR down for at least 2 seconds */
  419.  
  420. hangup_tty(tp)
  421. struct termio *tp;
  422. {
  423. #ifdef    HANGUP
  424.     (void) signal(SIGHUP, SIG_IGN);
  425.     tp->c_cflag &= ~CBAUD;
  426.     tp->c_cflag |= B0;
  427.     (void) ioctl(0, TCSETA, tp);
  428.     (void) signal(SIGHUP, SIG_DFL);
  429.     (void) sleep(2);
  430. #endif
  431. }
  432.  
  433. /* termio_init - initialize termio settings */
  434.  
  435. termio_init(tp, speed)
  436. struct termio *tp;
  437. int     speed;
  438. {
  439.  
  440.     /*
  441.      * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
  442.      * Special characters are set after we have read the login name; all
  443.      * reads will be done in raw mode anyway.
  444.      */
  445.  
  446.     tp->c_cflag = CS8 | HUPCL | CREAD | speed;
  447.     tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
  448.     tp->c_cc[VMIN] = 1;
  449.     tp->c_cc[VTIME] = 0;
  450.     (void) ioctl(0, TCSETA, tp);
  451. }
  452.  
  453. /* auto_baud - extract baud rate from modem status message */
  454.  
  455. auto_baud(tp)
  456. struct termio *tp;
  457. {
  458.     int     speed;
  459.     int     vmin;
  460.     int     iflag;
  461.     char    buf[BUFSIZ];
  462.     char   *bp;
  463.     int     nread;
  464.  
  465.     /*
  466.      * This works only if the modem produces its status code AFTER raising
  467.      * the DCD line, and if the computer is fast enough to set the proper
  468.      * baud rate before the message has gone by. We expect a message of the
  469.      * following format:
  470.      * 
  471.      * <junk><number><junk>
  472.      * 
  473.      * The number is interpreted as the baud rate of the incoming call. If the
  474.      * modem does not tell us the baud rate within one second we will keep
  475.      * using the current baud rate. It is advisable to enable baud-rate
  476.      * cycling (-a option) if the processing of modem status messages is
  477.      * enabled.
  478.      */
  479.  
  480.     /* Use 7-bit characters, don't block if input queue is empty */
  481.  
  482.     iflag = tp->c_iflag;
  483.     tp->c_iflag |= ISTRIP;            /* enable 8th-bit stripping */
  484.     vmin = tp->c_cc[VMIN];
  485.     tp->c_cc[VMIN] = 0;                /* don't block if queue empty */
  486.     (void) ioctl(0, TCSETA, tp);
  487.  
  488.     /*
  489.      * Wait for a while, then read everything the modem has said so far and
  490.      * try to extract the speed of the dial-in call.
  491.      */
  492.  
  493.     (void) sleep(1);
  494.     if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
  495.     buf[nread] = '\0';
  496.     for (bp = buf; bp < buf + nread; bp++) {
  497.         if (isascii(*bp) && isdigit(*bp)) {
  498.         if (speed = bcode(bp)) {
  499.             tp->c_cflag &= ~CBAUD;
  500.             tp->c_cflag |= speed;
  501.         }
  502.         break;
  503.         }
  504.     }
  505.     }
  506.     /* Restore settings */
  507.  
  508.     tp->c_iflag = iflag;
  509.     tp->c_cc[VMIN] = vmin;
  510.     (void) ioctl(0, TCSETA, tp);
  511. }
  512.  
  513. /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
  514.  
  515. do_prompt(op, tp)
  516. struct options *op;
  517. struct termio *tp;
  518. {
  519. #ifdef    ISSUE
  520.     int     fd;
  521.     int     oflag;
  522.     int     n;
  523.     char    buf[BUFSIZ];
  524. #endif
  525.  
  526.     write(1, "\r\n", 2);            /* Start a new line */
  527. #ifdef    ISSUE                    /* Optional: show /etc/issue */
  528.     if ((op->flags & F_ISSUE) && (fd = open(ISSUE, 0)) >= 0) {
  529.     oflag = tp->c_oflag;            /* Save current setting */
  530.     tp->c_oflag |= (ONLCR | OPOST);        /* Map NL in output to CR-NL */
  531.     (void) ioctl(0, TCSETAW, tp);
  532.     while ((n = read(fd, buf, sizeof(buf))) > 0)
  533.         (void) write(1, buf, n);
  534.     tp->c_oflag = oflag;            /* Restore settings */
  535.     (void) ioctl(0, TCSETAW, tp);        /* Wait till output gone */
  536.     (void) close(fd);
  537.     }
  538. #endif
  539.     (void) write(1, LOGIN, sizeof(LOGIN) - 1);    /* Always show login prompt */
  540. }
  541.  
  542. /* next_speed - select next baud rate */
  543.  
  544. next_speed(tp, op)
  545. struct termio *tp;
  546. struct options *op;
  547. {
  548.     op->curspeed = (op->curspeed + 1) % op->numspeed;
  549.     tp->c_cflag &= ~CBAUD;
  550.     tp->c_cflag |= op->speeds[op->curspeed];
  551.     (void) ioctl(0, TCSETA, tp);
  552. }
  553.  
  554. /* get_logname - get user name, establish parity, speed, erase, kill, eol */
  555.  
  556. char   *get_logname(op, cp, tp)
  557. struct options *op;
  558. struct chardata *cp;
  559. struct termio *tp;
  560. {
  561.     char    logname[BUFSIZ];
  562.     char   *bp;
  563.     char    c;                /* input character, full eight bits */
  564.     char    ascval;            /* low 7 bits of input character */
  565.     int     bits;            /* # of "1" bits per character */
  566.     int     mask;            /* mask with 1 bit up */
  567.     static char *erase[] = {        /* backspace-space-backspace */
  568.     "\010\040\010",            /* space parity */
  569.     "\010\040\010",            /* odd parity */
  570.     "\210\240\210",            /* even parity */
  571.     "\210\240\210",            /* no parity */
  572.     };
  573.  
  574.     /* Initialize kill, erase, parity etcetera (also after switching speeds) */
  575.  
  576.     cp->kill = DEF_KILL;
  577.     cp->erase = DEF_ERASE;
  578.     cp->parity = 0;
  579.  
  580.     /* Flush any pending input */
  581.  
  582.     (void) ioctl(0, TCFLSH, (struct termio *) 0);
  583.  
  584.     /* Read a login name */
  585.  
  586.     for (*logname = 0; *logname == 0; /* void */ ) {
  587.  
  588.     /* Write issue file and prompt, with "parity" bit == 0 */
  589.  
  590.     do_prompt(op, tp);
  591.  
  592.     /* Read name, watch for break, parity, erase, kill, end-of-line */
  593.  
  594.     for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
  595.         if (read(0, &c, 1) < 1)
  596.         error("%s: read error", op->tty);
  597.  
  598.         /* Do BREAK handling elsewhere */
  599.  
  600.         if ((c == 0) && op->numspeed > 1)
  601.         return (0);
  602.  
  603.         /* Do parity bit handling */
  604.  
  605.         if (c != (ascval = (c & 0177))) {    /* "parity" bit on ? */
  606.         for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
  607.             if (mask & ascval)
  608.             bits++;            /* count "1" bits */
  609.         cp->parity |= ((bits & 1) ? 1 : 2);
  610.         }
  611.         /* Do erase, kill and end-of-line processing */
  612.  
  613.         switch (ascval) {
  614.         case CR:
  615.         case NL:
  616.         *bp = 0;            /* terminate logname */
  617.         cp->eol = ascval;        /* set end-of-line char */
  618.         break;
  619.         case BS:
  620.         case DEL:
  621.         case '#':
  622.         cp->erase = ascval;        /* set erase character */
  623.         if (bp > logname) {
  624.             (void) write(1, erase[cp->parity], 3);
  625.             bp--;
  626.         }
  627.         break;
  628.         case CTL('U'):
  629.         case '@':
  630.         cp->kill = ascval;        /* set kill character */
  631.         while (bp > logname) {
  632.             (void) write(1, erase[cp->parity], 3);
  633.             bp--;
  634.         }
  635.         break;
  636.         case CTL('D'):
  637.         exit(0);
  638.         default:
  639.         if (!isascii(ascval) || !isprint(ascval)) {
  640.              /* ignore garbage characters */ ;
  641.         } else if (bp - logname >= sizeof(logname) - 1) {
  642.             error("%s: input overrun", op->tty);
  643.         } else {
  644.             (void) write(1, &c, 1);    /* echo the character */
  645.             *bp++ = ascval;        /* and store it */
  646.         }
  647.         break;
  648.         }
  649.     }
  650.     }
  651.     cp->capslock = caps_lock(logname);        /* upper case w/o lower case? */
  652.     return (logname);
  653. }
  654.  
  655. /* termio_final - set the final tty mode bits */
  656.  
  657. termio_final(tp, cp)
  658. struct termio *tp;
  659. struct chardata *cp;
  660. {
  661.     /* General terminal-independent stuff */
  662.  
  663.     tp->c_iflag |= IXON | IXOFF;        /* 2-way flow control */
  664.     tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK;
  665.     tp->c_oflag |= OPOST;
  666.     tp->c_cc[VEOF] = DEF_EOF;
  667.     tp->c_cc[VEOL] = DEF_EOL;
  668.     tp->c_cc[VINTR] = DEF_INTR;
  669.     tp->c_cc[VQUIT] = DEF_QUIT;
  670.     tp->c_cc[VKILL] = DEF_KILL;
  671.     tp->c_cc[VERASE] = DEF_ERASE;
  672.     tp->c_cc[VSWTCH] = DEF_SWITCH;
  673.  
  674.     /* Account for special characters seen in input */
  675.  
  676.     if (cp->eol == CR) {
  677.     tp->c_iflag |= ICRNL;            /* map CR in input to NL */
  678.     tp->c_oflag |= ONLCR;            /* map NL in output to CR-NL */
  679.     }
  680.     tp->c_cc[VERASE] = cp->erase;        /* set erase character */
  681.     tp->c_cc[VKILL] = cp->kill;            /* set kill character */
  682.  
  683.     /* Account for the presence or absence of parity bits in input */
  684.  
  685.     switch (cp->parity) {
  686.     case 0:                    /* space (always 0) parity */
  687.     break;
  688.     case 1:                    /* odd parity */
  689.     tp->c_cflag |= PARODD;
  690.     /* FALLTHROUGH */
  691.     case 2:                    /* even parity */
  692.     tp->c_cflag |= PARENB;
  693.     tp->c_iflag |= INPCK | ISTRIP;
  694.     /* FALLTHROUGH */
  695.     case (1 | 2):                /* no parity bit */
  696.     tp->c_cflag &= ~CSIZE;
  697.     tp->c_cflag |= CS7;
  698.     break;
  699.     }
  700.     /* Account for upper case without lower case */
  701.  
  702.     if (cp->capslock) {
  703.     tp->c_iflag |= IUCLC;
  704.     tp->c_lflag |= XCASE;
  705.     tp->c_oflag |= OLCUC;
  706.     }
  707.     /* Finally, make the new settings effective */
  708.  
  709.     (void) ioctl(0, TCSETA, tp);
  710. }
  711.  
  712. /* caps_lock - string contains upper case without lower case */
  713.  
  714. caps_lock(s)
  715. char   *s;
  716. {
  717.     int     hascaps;
  718.  
  719.     for (hascaps = 0; *s; s++) {
  720.     if (islower(*s))
  721.         return (0);
  722.     if (hascaps == 0)
  723.         hascaps = isupper(*s);
  724.     }
  725.     return (hascaps);
  726. }
  727.  
  728. /* bcode - convert speed string to speed code; return 0 on failure */
  729.  
  730. bcode(s)
  731. char   *s;
  732. {
  733.     struct Speedtab {
  734.     int     speed;
  735.     int     code;
  736.     };
  737.     static struct Speedtab speedtab[] = {
  738.     50, B50,
  739.     75, B75,
  740.     110, B110,
  741.     134, B134,
  742.     150, B150,
  743.     200, B200,
  744.     300, B300,
  745.     600, B600,
  746.     1200, B1200,
  747.     1800, B1800,
  748.     2400, B2400,
  749.     4800, B4800,
  750.     9600, B9600,
  751.     19200, EXTA,
  752.     0, 0,
  753.     };
  754.     struct Speedtab *sp;
  755.     int     speed = atoi(s);
  756.  
  757.     for (sp = speedtab; sp->speed; sp++)
  758.     if (sp->speed == speed)
  759.         return (sp->code);
  760.     return (0);
  761. }
  762.  
  763. /* usage - explain */
  764.  
  765. usage()
  766. {
  767.     static char args[] =
  768.     "[-a alternate_rates] [-h] [-i] [-m] [-t timeout] line baud_rate";
  769.  
  770.     error("usage: %s %s", progname, args);
  771. }
  772.  
  773. /* error - report errors to the console; only understands %s */
  774.  
  775. #define    str2cpy(b,s1,s2)    strcat(strcpy(b,s1),s2)
  776.  
  777. /* VARARGS */
  778.  
  779. error(va_alist)
  780. va_dcl
  781. {
  782.     va_list ap;
  783.     char   *fmt;
  784.     int     fd;
  785.     int     err = errno;
  786.     char    buf[BUFSIZ];
  787.     char   *bp;
  788.  
  789.     if ((fd = open("/dev/console", 1)) >= 0) {
  790.     (void) str2cpy(buf, progname, ": ");
  791.     bp = buf + strlen(buf);
  792.  
  793.     /*
  794.      * %s expansion is done by hand. The program would become three times
  795.      * as big if we would use the stdio library...
  796.      */
  797.  
  798.     va_start(ap);
  799.     fmt = va_arg(ap, char *);
  800.     while (*fmt) {
  801.         if (strncmp(fmt, "%s", 2) == 0) {
  802.         (void) strcat(bp, va_arg(ap, char *));
  803.         bp += strlen(bp);
  804.         fmt += 2;
  805.         } else {
  806.         *bp++ = *fmt++;
  807.         }
  808.     }
  809.     *bp = 0;
  810.     va_end(ap);
  811.  
  812.     /* Add system error message if errno was set */
  813.  
  814.     if (err)
  815.         (void) str2cpy(bp, ": ", sys_errlist[errno]);
  816.  
  817.     /* Terminate with CR-LF since the console mode is unknown */
  818.  
  819.     (void) strcat(bp, "\r\n");
  820.     (void) write(fd, buf, strlen(buf));
  821.     (void) close(fd);
  822.     }
  823.     (void) sleep(5);                /* be kind to init */
  824.     exit(1);
  825. }
  826.