home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3625 / agetty.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-14  |  24.3 KB  |  901 lines

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