home *** CD-ROM | disk | FTP | other *** search
/ Chip 1995 March / CHIP3.mdf / slackwar / a / util / util-lin.10 / util-lin / util-linux-1.10 / agetty.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-18  |  26.9 KB  |  1,082 lines

  1. /* agetty.c - another getty program for Linux. By W. Z. Venema 1989
  2.    Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
  3.    This program in freely distributable. The entire man-page used to
  4.    be here. Now read the real man-page agetty.8 instead.
  5. */
  6.  
  7. #ifndef    lint
  8. char sccsid[] = "@(#) agetty.c 1.29 9/1/91 23:22:00";
  9. #endif
  10.  
  11. #include <stdio.h>
  12. #include <unistd.h>
  13. #include <stdlib.h>
  14. #include <sys/ioctl.h>
  15. #include <termio.h>
  16. #include <signal.h>
  17. #include <errno.h>
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <fcntl.h>
  21. #include <varargs.h>
  22. #include <ctype.h>
  23. #include <utmp.h>
  24. #include <getopt.h>
  25. #include <memory.h>
  26. #include <sys/file.h>
  27.  
  28. #ifdef linux
  29. #include "pathnames.h"
  30. #include <sys/param.h>
  31. #define USE_SYSLOG
  32. #endif
  33.  
  34.  /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
  35.  
  36. #ifdef    USE_SYSLOG
  37. #include <syslog.h>
  38. extern void closelog();
  39. #endif
  40.  
  41.  /*
  42.   * Some heuristics to find out what environment we are in: if it is not
  43.   * System V, assume it is SunOS 4.
  44.   */
  45.  
  46. #ifdef LOGIN_PROCESS            /* defined in System V utmp.h */
  47. #define    SYSV_STYLE            /* select System V style getty */
  48. #endif
  49.  
  50.  /*
  51.   * Things you may want to modify.
  52.   * 
  53.   * If ISSUE is not defined, agetty will never display the contents of the
  54.   * /etc/issue file. You will not want to spit out large "issue" files at the
  55.   * wrong baud rate. Relevant for System V only.
  56.   * 
  57.   * You may disagree with the default line-editing etc. characters defined
  58.   * below. Note, however, that DEL cannot be used for interrupt generation
  59.   * and for line editing at the same time.
  60.   */
  61.  
  62. #ifdef    SYSV_STYLE
  63. #define    ISSUE "/etc/issue"        /* displayed before the login prompt */
  64. #include <sys/utsname.h>
  65. #include <time.h>
  66. #endif
  67.  
  68. #define LOGIN " login: "        /* login prompt */
  69.  
  70. /* Some shorthands for control characters. */
  71.  
  72. #define CTL(x)        (x ^ 0100)    /* Assumes ASCII dialect */
  73. #define    CR        CTL('M')    /* carriage return */
  74. #define    NL        CTL('J')    /* line feed */
  75. #define    BS        CTL('H')    /* back space */
  76. #define    DEL        CTL('?')    /* delete */
  77.  
  78. /* Defaults for line-editing etc. characters; you may want to change this. */
  79.  
  80. #define DEF_ERASE    DEL        /* default erase character */
  81. #define DEF_INTR    CTL('C')    /* default interrupt character */
  82. #define DEF_QUIT    CTL('\\')    /* default quit char */
  83. #define DEF_KILL    CTL('U')    /* default kill char */
  84. #define DEF_EOF        CTL('D')    /* default EOF char */
  85. #define DEF_EOL        0
  86. #define DEF_SWITCH    0        /* default switch char */
  87.  
  88.  /*
  89.   * SunOS 4.1.1 termio is broken. We must use the termios stuff instead,
  90.   * because the termio -> termios translation does not clear the termios
  91.   * CIBAUD bits. Therefore, the tty driver would sometimes report that input
  92.   * baud rate != output baud rate. I did not notice that problem with SunOS
  93.   * 4.1. We will use termios where available, and termio otherwise.
  94.   */
  95.  
  96. /* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set
  97.    properly, but all is well if we use termios?! */
  98.  
  99. #ifdef    TCGETS
  100. #undef    TCGETA
  101. #undef    TCSETA
  102. #undef    TCSETAW
  103. #define    termio    termios
  104. #define    TCGETA    TCGETS
  105. #define    TCSETA    TCSETS
  106. #define    TCSETAW    TCSETSW
  107. #endif
  108.  
  109.  /*
  110.   * This program tries to not use the standard-i/o library.  This keeps the
  111.   * executable small on systems that do not have shared libraries (System V
  112.   * Release <3).
  113.   */
  114.  
  115. #define    BUFSIZ        1024
  116.  
  117.  /*
  118.   * When multiple baud rates are specified on the command line, the first one
  119.   * we will try is the first one specified.
  120.   */
  121.  
  122. #define    FIRST_SPEED    0
  123.  
  124. /* Storage for command-line options. */
  125.  
  126. #define    MAX_SPEED    10        /* max. nr. of baud rates */
  127.  
  128. struct options {
  129.     int     flags;            /* toggle switches, see below */
  130.     int     timeout;            /* time-out period */
  131.     char   *login;            /* login program */
  132.     int     numspeed;            /* number of baud rates to try */
  133.     int     speeds[MAX_SPEED];        /* baud rates to be tried */
  134.     char   *tty;            /* name of tty */
  135. };
  136.  
  137. #define    F_PARSE        (1<<0)        /* process modem status messages */
  138. #define    F_ISSUE        (1<<1)        /* display /etc/issue */
  139. #define    F_RTSCTS    (1<<2)        /* enable RTS/CTS flow control */
  140. #define F_LOCAL        (1<<3)        /* force local */
  141.  
  142. /* Storage for things detected while the login name was read. */
  143.  
  144. struct chardata {
  145.     int     erase;            /* erase character */
  146.     int     kill;            /* kill character */
  147.     int     eol;            /* end-of-line character */
  148.     int     parity;            /* what parity did we see */
  149.     int     capslock;            /* upper case without lower case */
  150. };
  151.  
  152. /* Initial values for the above. */
  153.  
  154. struct chardata init_chardata = {
  155.     DEF_ERASE,                /* default erase character */
  156.     DEF_KILL,                /* default kill character */
  157.     0,                    /* always filled in at runtime */
  158.     0,                    /* space parity */
  159.     0,                    /* always filled in at runtime */
  160. };
  161.  
  162. #define P_(s) ()
  163. void parse_args P_((int argc, char **argv, struct options *op));
  164. void parse_speeds P_((struct options *op, char *arg));
  165. void update_utmp P_((char *line));
  166. void open_tty P_((char *tty, struct termio *tp, int local));
  167. void termio_init P_((struct termio *tp, int speed, int local));
  168. void auto_baud P_((struct termio *tp));
  169. void do_prompt P_((struct options *op, struct termio *tp));
  170. void next_speed P_((struct termio *tp, struct options *op));
  171. char *get_logname P_((struct options *op, struct chardata *cp, struct termio *tp));
  172. void termio_final P_((struct options *op, struct termio *tp, struct chardata *cp));
  173. int caps_lock P_((char *s));
  174. int bcode P_((char *s));
  175. void usage P_((void));
  176. void error P_((int va_alist));
  177. #undef P_
  178.  
  179. /* The following is used for understandable diagnostics. */
  180.  
  181. char *progname;
  182.  
  183. /* ... */
  184. #ifdef DEBUGGING
  185. #define debug(s) fprintf(dbf,s); fflush(dbf)
  186. FILE *dbf;
  187. #else
  188. #define debug(s) /* nothing */
  189. #endif
  190.  
  191. int
  192. main(argc, argv)
  193.      int     argc;
  194.      char  **argv;
  195. {
  196.     char   *logname;            /* login name, given to /bin/login */
  197.     char   *get_logname();
  198.     struct chardata chardata;        /* set by get_logname() */
  199.     struct termio termio;        /* terminal mode bits */
  200.     static struct options options = {
  201.     F_ISSUE,            /* show /etc/issue (SYSV_STYLE) */
  202.     0,                /* no timeout */
  203.     _PATH_LOGIN,            /* default login program */
  204.     0,                /* no baud rates known yet */
  205.     };
  206.  
  207.     /* The BSD-style init command passes us a useless process name. */
  208.  
  209. #ifdef    SYSV_STYLE
  210.     progname = argv[0];
  211. #else
  212.     progname = "agetty";
  213. #endif
  214.  
  215. #ifdef DEBUGGING
  216.     dbf = fopen("/dev/tty1", "w");
  217.  
  218.     {    int i;
  219.     
  220.         for(i = 1; i < argc; i++) {
  221.             debug(argv[i]);
  222.         }
  223.     }
  224. #endif
  225.  
  226.     /* Parse command-line arguments. */
  227.  
  228.     parse_args(argc, argv, &options);
  229.  
  230. #ifdef linux
  231.     setsid();
  232. #endif
  233.     
  234.     /* Update the utmp file. */
  235.  
  236. #ifdef    SYSV_STYLE
  237.     update_utmp(options.tty);
  238. #endif
  239.  
  240.     /* Open the tty as standard { input, output, error }. */
  241.     open_tty(options.tty, &termio, options.flags & F_LOCAL);
  242.  
  243. #ifdef linux
  244.     {
  245.         int iv;
  246.         
  247.         iv = getpid();
  248.         (void) ioctl(0, TIOCSPGRP, &iv);
  249.     }
  250. #endif
  251.     /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
  252.  
  253.     termio_init(&termio, options.speeds[FIRST_SPEED], options.flags & F_LOCAL);
  254.     
  255.     /* Optionally detect the baud rate from the modem status message. */
  256.  
  257.     if (options.flags & F_PARSE)
  258.     auto_baud(&termio);
  259.  
  260.     /* Set the optional timer. */
  261.  
  262.     if (options.timeout)
  263.     (void) alarm((unsigned) options.timeout);
  264.  
  265.     /* Read the login name. */
  266.  
  267.     while ((logname = get_logname(&options, &chardata, &termio)) == 0)
  268.     next_speed(&termio, &options);
  269.  
  270.     /* Disable timer. */
  271.  
  272.     if (options.timeout)
  273.     (void) alarm(0);
  274.  
  275.     /* Finalize the termio settings. */
  276.  
  277.     termio_final(&options, &termio, &chardata);
  278.  
  279.     /* Now the newline character should be properly written. */
  280.  
  281.     (void) write(1, "\n", 1);
  282.  
  283.     /* Let the login program take care of password validation. */
  284.  
  285.     (void) execl(options.login, options.login, "--", logname, (char *) 0);
  286.     error("%s: can't exec %s: %m", options.tty, options.login);
  287.     exit(0);  /* quiet GCC */
  288. }
  289.  
  290. /* parse-args - parse command-line arguments */
  291.  
  292. void
  293. parse_args(argc, argv, op)
  294.      int     argc;
  295.      char  **argv;
  296.      struct options *op;
  297. {
  298.     extern char *optarg;        /* getopt */
  299.     extern int optind;            /* getopt */
  300.     int     c;
  301.  
  302.     while (isascii(c = getopt(argc, argv, "Lhil:mt:"))) {
  303.     switch (c) {
  304.     case 'L':                /* force local */
  305.          op->flags |= F_LOCAL;
  306.          break;
  307.     case 'h':                /* enable h/w flow control */
  308.         op->flags |= F_RTSCTS;
  309.         break;
  310.     case 'i':                /* do not show /etc/issue */
  311.         op->flags &= ~F_ISSUE;
  312.         break;
  313.     case 'l':
  314.         op->login = optarg;            /* non-default login program */
  315.         break;
  316.     case 'm':                /* parse modem status message */
  317.         op->flags |= F_PARSE;
  318.         break;
  319.     case 't':                /* time out */
  320.         if ((op->timeout = atoi(optarg)) <= 0)
  321.         error("bad timeout value: %s", optarg);
  322.         break;
  323.     default:
  324.         usage();
  325.     }
  326.     }
  327.      debug("after getopt loop\n");
  328.     if (argc != optind + 2)            /* check parameter count */
  329.     usage();
  330.  
  331.     /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
  332.     if('0' <= argv[optind][0] && argv[optind][0] <= '9') {
  333.     /* a number first, assume it's a speed (BSD style) */
  334.     parse_speeds(op, argv[optind++]);    /* baud rate(s) */
  335.     op->tty = argv[optind];            /* tty name */
  336.     } else {
  337.     op->tty = argv[optind++];        /* tty name */
  338.     parse_speeds(op, argv[optind]);        /* baud rate(s) */
  339.     }
  340.  
  341.     debug("exiting parseargs\n");
  342. }
  343.  
  344. /* parse_speeds - parse alternate baud rates */
  345.  
  346. void
  347. parse_speeds(op, arg)
  348.      struct options *op;
  349.      char   *arg;
  350. {
  351.     char   *strtok();
  352.     char   *cp;
  353.  
  354.     debug("entered parse_speeds\n");
  355.     for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
  356.     if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
  357.         error("bad speed: %s", cp);
  358.     if (op->numspeed > MAX_SPEED)
  359.         error("too many alternate speeds");
  360.     }
  361.     debug("exiting parsespeeds\n");
  362. }
  363.  
  364. #ifdef    SYSV_STYLE
  365.  
  366. /* update_utmp - update our utmp entry */
  367. void
  368. update_utmp(line)
  369.      char   *line;
  370. {
  371.     struct utmp ut;
  372.     long    ut_size = sizeof(ut);    /* avoid nonsense */
  373.     int     ut_fd;
  374.     int     mypid = getpid();
  375.     long    time();
  376.     long    lseek();
  377.     char   *strncpy();
  378.  
  379.     /*
  380.      * The utmp file holds miscellaneous information about things started by
  381.      * /etc/init and other system-related events. Our purpose is to update
  382.      * the utmp entry for the current process, in particular the process type
  383.      * and the tty line we are listening to. Return successfully only if the
  384.      * utmp file can be opened for update, and if we are able to find our
  385.      * entry in the utmp file.
  386.      */
  387.  
  388. #ifdef linux
  389.     utmpname(_PATH_UTMP);
  390.         memset(&ut, 0, sizeof(ut));
  391.     (void) strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
  392.     (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
  393.     (void) strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
  394.     (void) time(&ut.ut_time);
  395.     ut.ut_type = LOGIN_PROCESS;
  396.     ut.ut_pid = mypid;
  397.  
  398.     pututline(&ut);
  399.     endutent();
  400.  
  401.         if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
  402.         flock(ut_fd, LOCK_EX);
  403.         write(ut_fd, &ut, sizeof(ut));
  404.         flock(ut_fd, LOCK_UN);
  405.         close(ut_fd);
  406.     }
  407. #else
  408.     if ((ut_fd = open(UTMP_FILE, 2)) < 0) {
  409.     error("%s: open for update: %m", UTMP_FILE);
  410.     } else {
  411.     while (read(ut_fd, (char *) &ut, sizeof(ut)) == sizeof(ut)) {
  412.         if (ut.ut_type == INIT_PROCESS && ut.ut_pid == mypid) {
  413.         ut.ut_type = LOGIN_PROCESS;
  414.         ut.ut_time = time((long *) 0);
  415.         (void) strncpy(ut.ut_name, "LOGIN", sizeof(ut.ut_name));
  416.         (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
  417.         (void) lseek(ut_fd, -ut_size, 1);
  418.         (void) write(ut_fd, (char *) &ut, sizeof(ut));
  419.         (void) close(ut_fd);
  420.         return;
  421.         }
  422.     }
  423.     error("%s: no utmp entry", line);
  424.     }
  425. #endif /* linux */
  426. }
  427.  
  428. #endif
  429.  
  430. /* open_tty - set up tty as standard { input, output, error } */
  431. void
  432. open_tty(tty, tp, local)
  433.      char   *tty;
  434.      struct termio *tp;
  435.      int    local;
  436. {
  437.     /* Get rid of the present standard { output, error} if any. */
  438.  
  439.     (void) close(1);
  440.     (void) close(2);
  441.     errno = 0;                    /* ignore above errors */
  442.  
  443.     /* Set up new standard input, unless we are given an already opened port. */
  444.  
  445.     if (strcmp(tty, "-")) {
  446.     struct stat st;
  447.  
  448.     /* Sanity checks... */
  449.  
  450.     if (chdir("/dev"))
  451.         error("/dev: chdir() failed: %m");
  452.     if (stat(tty, &st) < 0)
  453.         error("/dev/%s: %m", tty);
  454.     if ((st.st_mode & S_IFMT) != S_IFCHR)
  455.         error("/dev/%s: not a character device", tty);
  456.  
  457.     /* Open the tty as standard input. */
  458.  
  459.     (void) close(0);
  460.     errno = 0;                /* ignore close(2) errors */
  461.  
  462.     if (open(tty, (local ? O_RDWR|O_NONBLOCK : O_RDWR), 0) != 0)
  463.         error("/dev/%s: cannot open as standard input: %m", tty);
  464.  
  465.     } else {
  466.  
  467.     /*
  468.      * Standard input should already be connected to an open port. Make
  469.      * sure it is open for read/write.
  470.      */
  471.  
  472.     if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
  473.         error("%s: not open for read/write", tty);
  474.     }
  475.  
  476.     /* Set up standard output and standard error file descriptors. */
  477.  
  478.     if (dup(0) != 1 || dup(0) != 2)        /* set up stdout and stderr */
  479.     error("%s: dup problem: %m", tty);    /* we have a problem */
  480.  
  481.     /*
  482.      * The following ioctl will fail if stdin is not a tty, but also when
  483.      * there is noise on the modem control lines. In the latter case, the
  484.      * common course of action is (1) fix your cables (2) give the modem more
  485.      * time to properly reset after hanging up. SunOS users can achieve (2)
  486.      * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
  487.      * 5 seconds seems to be a good value.
  488.      */
  489.  
  490.     if (ioctl(0, TCGETA, tp) < 0)
  491.     error("%s: ioctl: %m", tty);
  492.  
  493.     /*
  494.      * It seems to be a terminal. Set proper protections and ownership. Mode
  495.      * 0622 is suitable for SYSV <4 because /bin/login does not change
  496.      * protections. SunOS 4 login will change the protections to 0620 (write
  497.      * access for group tty) after the login has succeeded.
  498.      */
  499.  
  500.     (void) chown(tty, 0, 0);            /* root, sys */
  501.     (void) chmod(tty, 0622);            /* crw--w--w- */
  502.     errno = 0;                    /* ignore above errors */
  503. }
  504.  
  505. /* termio_init - initialize termio settings */
  506.  
  507. char gbuf[1024];
  508. char area[1024];
  509.  
  510. void
  511. termio_init(tp, speed, local)
  512.      struct termio *tp;
  513.      int     speed;
  514.      int    local;
  515. {
  516.  
  517.     /*
  518.      * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
  519.      * Special characters are set after we have read the login name; all
  520.      * reads will be done in raw mode anyway. Errors will be dealt with
  521.      * lateron.
  522.      */
  523. #ifdef linux
  524.     /* flush input and output queues, important for modems! */
  525.     (void) ioctl(0, TCFLSH, 2);
  526. #endif
  527.  
  528.     tp->c_cflag = CS8 | HUPCL | CREAD | speed;
  529.     if (local) {
  530.     tp->c_cflag |= CLOCAL;
  531.     }
  532.  
  533.     tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
  534.     tp->c_cc[VMIN] = 1;
  535.     tp->c_cc[VTIME] = 0;
  536.     (void) ioctl(0, TCSETA, tp);
  537.     if (local) {
  538.     (void) fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NDELAY);
  539.     }
  540.     debug("term_io 2\n");
  541. }
  542.  
  543. /* auto_baud - extract baud rate from modem status message */
  544. void
  545. auto_baud(tp)
  546.      struct termio *tp;
  547. {
  548.     int     speed;
  549.     int     vmin;
  550.     unsigned iflag;
  551.     char    buf[BUFSIZ];
  552.     char   *bp;
  553.     int     nread;
  554.  
  555.     /*
  556.      * This works only if the modem produces its status code AFTER raising
  557.      * the DCD line, and if the computer is fast enough to set the proper
  558.      * baud rate before the message has gone by. We expect a message of the
  559.      * following format:
  560.      * 
  561.      * <junk><number><junk>
  562.      * 
  563.      * The number is interpreted as the baud rate of the incoming call. If the
  564.      * modem does not tell us the baud rate within one second, we will keep
  565.      * using the current baud rate. It is advisable to enable BREAK
  566.      * processing (comma-separated list of baud rates) if the processing of
  567.      * modem status messages is enabled.
  568.      */
  569.  
  570.     /*
  571.      * Use 7-bit characters, don't block if input queue is empty. Errors will
  572.      * be dealt with lateron.
  573.      */
  574.  
  575.     iflag = tp->c_iflag;
  576.     tp->c_iflag |= ISTRIP;            /* enable 8th-bit stripping */
  577.     vmin = tp->c_cc[VMIN];
  578.     tp->c_cc[VMIN] = 0;                /* don't block if queue empty */
  579.     (void) ioctl(0, TCSETA, tp);
  580.  
  581.     /*
  582.      * Wait for a while, then read everything the modem has said so far and
  583.      * try to extract the speed of the dial-in call.
  584.      */
  585.  
  586.     (void) sleep(1);
  587.     if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
  588.     buf[nread] = '\0';
  589.     for (bp = buf; bp < buf + nread; bp++) {
  590.         if (isascii(*bp) && isdigit(*bp)) {
  591.         if (speed = bcode(bp)) {
  592.             tp->c_cflag &= ~CBAUD;
  593.             tp->c_cflag |= speed;
  594.         }
  595.         break;
  596.         }
  597.     }
  598.     }
  599.     /* Restore terminal settings. Errors will be dealt with lateron. */
  600.  
  601.     tp->c_iflag = iflag;
  602.     tp->c_cc[VMIN] = vmin;
  603.     (void) ioctl(0, TCSETA, tp);
  604. }
  605.  
  606. /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
  607. void
  608. do_prompt(op, tp)
  609.      struct options *op;
  610.      struct termio *tp;
  611. {
  612. #ifdef    ISSUE
  613.     FILE    *fd;
  614.     int     oflag;
  615.     char    c;
  616.     struct utsname uts;
  617.  
  618.     (void) uname(&uts);
  619. #endif
  620.  
  621.     (void) write(1, "\r\n", 2);            /* start a new line */
  622. #ifdef    ISSUE                    /* optional: show /etc/issue */
  623.     if ((op->flags & F_ISSUE) && (fd = fopen(ISSUE, "r"))) {
  624.     oflag = tp->c_oflag;            /* save current setting */
  625.     tp->c_oflag |= (ONLCR | OPOST);        /* map NL in output to CR-NL */
  626.     (void) ioctl(0, TCSETAW, tp);
  627.  
  628.  
  629.     while ((c = getc(fd)) != EOF)
  630.     {
  631.         if (c == '\\')
  632.           {
  633.         c = getc(fd);
  634.         
  635.         switch (c)
  636.           {
  637.           case 's':
  638.             (void) printf ("%s", uts.sysname);
  639.             break;
  640.             
  641.           case 'n':
  642.             (void) printf ("%s", uts.nodename);
  643.             break;
  644.             
  645.           case 'r':
  646.             (void) printf ("%s", uts.release);
  647.             break;
  648.             
  649.           case 'v':
  650.             (void) printf ("%s", uts.version);
  651.             break;
  652.             
  653.           case 'm':
  654.             (void) printf ("%s", uts.machine);
  655.             break;
  656.  
  657.           case 'o':
  658.             (void) printf ("%s", uts.domainname);
  659.             break;
  660.  
  661.           case 'd':
  662.           case 't':
  663.             {
  664.               char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
  665.                       "Fri", "Sat" };
  666.               char *month[] = { "Jan", "Feb", "Mar", "Apr", "May",
  667.                     "Jun", "Jul", "Aug", "Sep", "Oct",
  668.                     "Nov", "Dec" };
  669.               time_t now;
  670.               struct tm *tm;
  671.  
  672.               (void) time (&now);
  673.               tm = localtime(&now);
  674.  
  675.               if (c == 'd')
  676.             (void) printf ("%s %s %d  %d",
  677.                 weekday[tm->tm_wday], month[tm->tm_mon],
  678.                 tm->tm_mday, 
  679.                 tm->tm_year < 70 ? tm->tm_year + 2000 :
  680.                 tm->tm_year + 1900);
  681.               else
  682.             (void) printf ("%02d:%02d:%02d",
  683.                 tm->tm_hour, tm->tm_min, tm->tm_sec);
  684.               
  685.               break;
  686.             }
  687.  
  688.           case 'l':
  689.               (void) printf ("%s", op->tty);
  690.               break;
  691.  
  692.           case 'b':
  693.             {
  694.               char *zpeedz[] = { "0", "50", "75", "110", "134.5",
  695.                      "150", "200", "300", "600", "1200",
  696.                      "1800", "2400", "4800", "9600", 
  697.                      "19200", "38400" };
  698.  
  699.               (void) printf ("%s", zpeedz[tp->c_cflag & CBAUD]);
  700.               break;
  701.             }
  702.             
  703.           default:
  704.             (void) putchar(c);
  705.           }
  706.           }
  707.         else
  708.           (void) putchar(c);
  709.     }
  710.     fflush(stdout);
  711.  
  712.     tp->c_oflag = oflag;            /* restore settings */
  713.     (void) ioctl(0, TCSETAW, tp);        /* wait till output is gone */
  714.     (void) fclose(fd);
  715.     }
  716. #endif
  717. #ifdef linux
  718.     {
  719.         char hn[MAXHOSTNAMELEN+1];
  720.  
  721.         (void) gethostname(hn, MAXHOSTNAMELEN);
  722.         write(1, hn, strlen(hn));
  723.     }
  724. #endif        
  725.     (void) write(1, LOGIN, sizeof(LOGIN) - 1);    /* always show login prompt */
  726. }
  727.  
  728. /* next_speed - select next baud rate */
  729. void
  730. next_speed(tp, op)
  731.      struct termio *tp;
  732.      struct options *op;
  733. {
  734.     static int baud_index = FIRST_SPEED;/* current speed index */
  735.  
  736.     baud_index = (baud_index + 1) % op->numspeed;
  737.     tp->c_cflag &= ~CBAUD;
  738.     tp->c_cflag |= op->speeds[baud_index];
  739.     (void) ioctl(0, TCSETA, tp);
  740. }
  741.  
  742. /* get_logname - get user name, establish parity, speed, erase, kill, eol */
  743.  
  744. char   *get_logname(op, cp, tp)
  745.      struct options *op;
  746.      struct chardata *cp;
  747.      struct termio *tp;
  748. {
  749.     char    logname[BUFSIZ];
  750.     char   *bp;
  751.     char    c;                /* input character, full eight bits */
  752.     char    ascval;            /* low 7 bits of input character */
  753.     int     bits;            /* # of "1" bits per character */
  754.     int     mask;            /* mask with 1 bit up */
  755.     static char *erase[] = {        /* backspace-space-backspace */
  756.     "\010\040\010",            /* space parity */
  757.     "\010\040\010",            /* odd parity */
  758.     "\210\240\210",            /* even parity */
  759.     "\210\240\210",            /* no parity */
  760.     };
  761.  
  762.     /* Initialize kill, erase, parity etc. (also after switching speeds). */
  763.  
  764.     *cp = init_chardata;
  765.  
  766.     /* Flush pending input (esp. after parsing or switching the baud rate). */
  767.  
  768.     (void) sleep(1);
  769.     (void) ioctl(0, TCFLSH, (struct termio *) 0);
  770.  
  771.     /* Prompt for and read a login name. */
  772.  
  773.     for (*logname = 0; *logname == 0; /* void */ ) {
  774.  
  775.     /* Write issue file and prompt, with "parity" bit == 0. */
  776.  
  777.     do_prompt(op, tp);
  778.  
  779.     /* Read name, watch for break, parity, erase, kill, end-of-line. */
  780.  
  781.     for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
  782.  
  783.         /* Do not report trivial EINTR/EIO errors. */
  784.  
  785.         if (read(0, &c, 1) < 1) {
  786.         if (errno == EINTR || errno == EIO)
  787.             exit(0);
  788.         error("%s: read: %m", op->tty);
  789.         }
  790.         /* Do BREAK handling elsewhere. */
  791.  
  792.         if ((c == 0) && op->numspeed > 1)
  793.         return (0);
  794.  
  795.         /* Do parity bit handling. */
  796.  
  797.         if (c != (ascval = (c & 0177))) {    /* "parity" bit on ? */
  798.         for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
  799.             if (mask & ascval)
  800.             bits++;            /* count "1" bits */
  801.         cp->parity |= ((bits & 1) ? 1 : 2);
  802.         }
  803.         /* Do erase, kill and end-of-line processing. */
  804.  
  805.         switch (ascval) {
  806.         case CR:
  807.         case NL:
  808.         *bp = 0;            /* terminate logname */
  809.         cp->eol = ascval;        /* set end-of-line char */
  810.         break;
  811.         case BS:
  812.         case DEL:
  813.         case '#':
  814.         cp->erase = ascval;        /* set erase character */
  815.         if (bp > logname) {
  816.             (void) write(1, erase[cp->parity], 3);
  817.             bp--;
  818.         }
  819.         break;
  820.         case CTL('U'):
  821.         case '@':
  822.         cp->kill = ascval;        /* set kill character */
  823.         while (bp > logname) {
  824.             (void) write(1, erase[cp->parity], 3);
  825.             bp--;
  826.         }
  827.         break;
  828.         case CTL('D'):
  829.         exit(0);
  830.         default:
  831.         if (!isascii(ascval) || !isprint(ascval)) {
  832.              /* ignore garbage characters */ ;
  833.         } else if (bp - logname >= sizeof(logname) - 1) {
  834.             error("%s: input overrun", op->tty);
  835.         } else {
  836.             (void) write(1, &c, 1);    /* echo the character */
  837.             *bp++ = ascval;        /* and store it */
  838.         }
  839.         break;
  840.         }
  841.     }
  842.     }
  843.     /* Handle names with upper case and no lower case. */
  844.  
  845.     if (cp->capslock = caps_lock(logname)) {
  846.     for (bp = logname; *bp; bp++)
  847.         if (isupper(*bp))
  848.         *bp = tolower(*bp);        /* map name to lower case */
  849.     }
  850.     return (logname);
  851. }
  852.  
  853. /* termio_final - set the final tty mode bits */
  854. void
  855. termio_final(op, tp, cp)
  856.      struct options *op;
  857.      struct termio *tp;
  858.      struct chardata *cp;
  859. {
  860.     /* General terminal-independent stuff. */
  861.  
  862.     tp->c_iflag |= IXON | IXOFF;        /* 2-way flow control */
  863.     tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK| ECHOKE;
  864.       /* no longer| ECHOCTL | ECHOPRT*/
  865.     tp->c_oflag |= OPOST;
  866.     /* tp->c_cflag = 0; */
  867.     tp->c_cc[VINTR] = DEF_INTR;            /* default interrupt */
  868.     tp->c_cc[VQUIT] = DEF_QUIT;            /* default quit */
  869.     tp->c_cc[VEOF] = DEF_EOF;            /* default EOF character */
  870.     tp->c_cc[VEOL] = DEF_EOL;
  871. #ifdef linux
  872.     tp->c_cc[VSWTC] = DEF_SWITCH;        /* default switch character */
  873. #else
  874.     tp->c_cc[VSWTCH] = DEF_SWITCH;        /* default switch character */
  875. #endif
  876.  
  877.     /* Account for special characters seen in input. */
  878.  
  879.     if (cp->eol == CR) {
  880.     tp->c_iflag |= ICRNL;            /* map CR in input to NL */
  881.     tp->c_oflag |= ONLCR;            /* map NL in output to CR-NL */
  882.     }
  883.     tp->c_cc[VERASE] = cp->erase;        /* set erase character */
  884.     tp->c_cc[VKILL] = cp->kill;            /* set kill character */
  885.  
  886.     /* Account for the presence or absence of parity bits in input. */
  887.  
  888.     switch (cp->parity) {
  889.     case 0:                    /* space (always 0) parity */
  890.     break;
  891.     case 1:                    /* odd parity */
  892.     tp->c_cflag |= PARODD;
  893.     /* FALLTHROUGH */
  894.     case 2:                    /* even parity */
  895.     tp->c_cflag |= PARENB;
  896.     tp->c_iflag |= INPCK | ISTRIP;
  897.     /* FALLTHROUGH */
  898.     case (1 | 2):                /* no parity bit */
  899.     tp->c_cflag &= ~CSIZE;
  900.     tp->c_cflag |= CS7;
  901.     break;
  902.     }
  903.     /* Account for upper case without lower case. */
  904.  
  905.     if (cp->capslock) {
  906.     tp->c_iflag |= IUCLC;
  907.     tp->c_lflag |= XCASE;
  908.     tp->c_oflag |= OLCUC;
  909.     }
  910.     /* Optionally enable hardware flow control */
  911.  
  912. #ifdef    CRTSCTS
  913.     if (op->flags & F_RTSCTS)
  914.     tp->c_cflag |= CRTSCTS;
  915. #endif
  916.  
  917.     /* Finally, make the new settings effective */
  918.  
  919.     if (ioctl(0, TCSETA, tp) < 0)
  920.     error("%s: ioctl: TCSETA: %m", op->tty);
  921. }
  922.  
  923. /* caps_lock - string contains upper case without lower case */
  924. int
  925. caps_lock(s)
  926.      char   *s;
  927. {
  928.     int     capslock;
  929.  
  930.     for (capslock = 0; *s; s++) {
  931.     if (islower(*s))
  932.         return (0);
  933.     if (capslock == 0)
  934.         capslock = isupper(*s);
  935.     }
  936.     return (capslock);
  937. }
  938.  
  939. /* bcode - convert speed string to speed code; return 0 on failure */
  940. int
  941. bcode(s)
  942.      char   *s;
  943. {
  944.     struct Speedtab {
  945.     long    speed;
  946.     int     code;
  947.     };
  948.     static struct Speedtab speedtab[] = {
  949.     50, B50,
  950.     75, B75,
  951.     110, B110,
  952.     134, B134,
  953.     150, B150,
  954.     200, B200,
  955.     300, B300,
  956.     600, B600,
  957.     1200, B1200,
  958.     1800, B1800,
  959.     2400, B2400,
  960.     4800, B4800,
  961.     9600, B9600,
  962. #ifdef    B19200
  963.     19200, B19200,
  964. #endif
  965. #ifdef    B38400
  966.     38400, B38400,
  967. #endif
  968. #ifdef    EXTA
  969.     19200, EXTA,
  970. #endif
  971. #ifdef    EXTB
  972.     38400, EXTB,
  973. #endif
  974.     0, 0,
  975.     };
  976.     struct Speedtab *sp;
  977.     long    speed = atol(s);
  978.  
  979.     for (sp = speedtab; sp->speed; sp++)
  980.     if (sp->speed == speed)
  981.         return (sp->code);
  982.     return (0);
  983. }
  984.  
  985. /* usage - explain */
  986.  
  987. void
  988. usage()
  989. {
  990. #if defined(SYSV_STYLE) && !defined(linux)
  991.     static char msg[] =
  992.     "[-i] [-l login_program] [-m] [-t timeout] line baud_rate,...";
  993. #else
  994.     static char msg[] =
  995.     "[-h] [-l login_program] [-m] [-t timeout] baud_rate,... line";
  996. #endif
  997.  
  998.     error("usage: %s %s", progname, msg);
  999. }
  1000.  
  1001. /* error - report errors to console or syslog; only understands %s and %m */
  1002.  
  1003. #define    str2cpy(b,s1,s2)    strcat(strcpy(b,s1),s2)
  1004.  
  1005. /* VARARGS */
  1006. void
  1007. error(va_alist)
  1008.      va_dcl
  1009. {
  1010.     va_list ap;
  1011.     char   *fmt;
  1012. #ifndef    USE_SYSLOG
  1013.     int     fd;
  1014. #endif
  1015.     char    buf[BUFSIZ];
  1016.     char   *bp;
  1017.  
  1018.     char   *strcpy();
  1019.     char   *strcat();
  1020.  
  1021.     /*
  1022.      * If the diagnostic is reported via syslog(3), the process name is
  1023.      * automatically prepended to the message. If we write directly to
  1024.      * /dev/console, we must prepend the process name ourselves.
  1025.      */
  1026.  
  1027. #ifdef USE_SYSLOG
  1028.     buf[0] = '\0';
  1029.     bp = buf;
  1030. #else
  1031.     (void) str2cpy(buf, progname, ": ");
  1032.     bp = buf + strlen(buf);
  1033. #endif
  1034.  
  1035.     /*
  1036.      * %s expansion is done by hand. On a System V Release 2 system without
  1037.      * shared libraries and without syslog(3), linking with the the stdio
  1038.      * library would make the program three times as big...
  1039.      *
  1040.      * %m expansion is done here as well. Too bad syslog(3) does not have a
  1041.      * vsprintf() like interface.
  1042.      */
  1043.  
  1044.     va_start(ap);
  1045.     fmt = va_arg(ap, char *);
  1046.     while (*fmt) {
  1047.     if (strncmp(fmt, "%s", 2) == 0) {
  1048.         (void) strcpy(bp, va_arg(ap, char *));
  1049.         bp += strlen(bp);
  1050.         fmt += 2;
  1051.     } else if (strncmp(fmt, "%m", 2) == 0) {
  1052.         (void) strcpy(bp, sys_errlist[errno]);
  1053.         bp += strlen(bp);
  1054.         fmt += 2;
  1055.     } else {
  1056.         *bp++ = *fmt++;
  1057.     }
  1058.     }
  1059.     *bp = 0;
  1060.     va_end(ap);
  1061.  
  1062.     /*
  1063.      * Write the diagnostic directly to /dev/console if we do not use the
  1064.      * syslog(3) facility.
  1065.      */
  1066.  
  1067. #ifdef    USE_SYSLOG
  1068.     (void) openlog(progname, LOG_PID, LOG_AUTH);
  1069.     (void) syslog(LOG_ERR, "%s", buf);
  1070.     closelog();
  1071. #else
  1072.     /* Terminate with CR-LF since the console mode is unknown. */
  1073.     (void) strcat(bp, "\r\n");
  1074.     if ((fd = open("/dev/console", 1)) >= 0) {
  1075.     (void) write(fd, buf, strlen(buf));
  1076.     (void) close(fd);
  1077.     }
  1078. #endif
  1079.     (void) sleep((unsigned) 10);            /* be kind to init(8) */
  1080.     exit(1);
  1081. }
  1082.