home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume39 / agetty / part01 / agetty.c < prev    next >
C/C++ Source or Header  |  1993-08-23  |  30KB  |  1,076 lines

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