diff options
author | Karel Zak | 2011-03-02 13:57:00 +0100 |
---|---|---|
committer | Karel Zak | 2011-03-02 13:57:00 +0100 |
commit | cf56b8b507a98b76ceb0fe2a691997ef589cbb02 (patch) | |
tree | 9777b0bd5a4970fe9518b93910fe2ca30bb90af6 /term-utils | |
parent | build-sys: add term-utils/ (diff) | |
download | kernel-qcow2-util-linux-cf56b8b507a98b76ceb0fe2a691997ef589cbb02.tar.gz kernel-qcow2-util-linux-cf56b8b507a98b76ceb0fe2a691997ef589cbb02.tar.xz kernel-qcow2-util-linux-cf56b8b507a98b76ceb0fe2a691997ef589cbb02.zip |
build-sys: move agetty to term-utils
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'term-utils')
-rw-r--r-- | term-utils/Makefile.am | 10 | ||||
-rw-r--r-- | term-utils/README.modems-with-agetty | 76 | ||||
-rw-r--r-- | term-utils/agetty.8 | 307 | ||||
-rw-r--r-- | term-utils/agetty.c | 1306 |
4 files changed, 1699 insertions, 0 deletions
diff --git a/term-utils/Makefile.am b/term-utils/Makefile.am index 3602b8c80..a7459a0f2 100644 --- a/term-utils/Makefile.am +++ b/term-utils/Makefile.am @@ -6,3 +6,13 @@ sbin_PROGRAMS = usrsbin_exec_PROGRAMS = dist_man_MANS = +EXTRA_DIST = README.modems-with-agetty + +if BUILD_AGETTY +sbin_PROGRAMS += agetty +dist_man_MANS += agetty.8 +agetty_SOURCES = agetty.c +if !HAVE_LANGINFO +agetty_SOURCES += $(top_srcdir)/lib/langinfo.c +endif +endif diff --git a/term-utils/README.modems-with-agetty b/term-utils/README.modems-with-agetty new file mode 100644 index 000000000..44a611e2a --- /dev/null +++ b/term-utils/README.modems-with-agetty @@ -0,0 +1,76 @@ +25/10/95 Peter Orbaek <poe@daimi.aau.dk> + +Some notes for using agetty with modems + +Using a comms program to initialize the modem +--------------------------------------------- + +* Use kermit or minicom to initialize the modem to + + - be entirely quiet. + - don't do local echo in command mode. + - turn on DCD (carrier detect) only when there is a connection going. + - enable auto-answer. + - keep a constant computer/modem bitrate at all times. + - optionally save this setup as the modem startup configuration. + +* Run agetty on the appropriate ttySn port with the arguments: + * -w to wait for a CR or LF before writing the /etc/issue message + * computer/modem bitrate + * the tty name. + +Example from my modem setup, an old 2400 bps SupraModem using Hayes standard +AT commands. + +Initialize modem using kermit with the commands + + AT E0 Q1 &D2 &C1 S0=1 &W0 + +to + - turn off local echo from modem when in command mode (E0). + - disable all result codes from modem (Q1). + - make an on/off transition on the DTR line make the modem + disconnect and go into command mode (&D2). + - make the computer/modem DCD line track the modem/modem + carrier detect signal, i.e. no connection means no + carrier detect signal to the computer (&C1). + - enable auto-answer after the first ring (S0=1). + - store the configuration as the start configuration (&W0). + +The commands on your modem to achieve the same setup may vary, especially +the &D2 and &C1 commands may not be entirely standard. + +Exit kermit/minicom. + +Put the command + + /sbin/agetty -w 2400 ttyS1 + +in the command field of the appropriate line in /etc/inittab to start +agetty on /dev/ttyS1 with a 2400 bps speed between modem and computer. + +Initializing the modem with agetty +---------------------------------- + +Use the agetty -I command line option to specify a modem init string, like +for the same setup as above, use the following agetty command in your +/etc/inittab. + + /sbin/agetty -w -I 'ATE0Q1&D2&C1S0=1\015' 2400 ttyS1 + +The final \015 is an octal coding of the carriage return character +ending the command string. + +If you're using simpleinit (part of this package) instead of the SYSV +compatible init (you're most likely using the SYSV one!) then you must +remove the single quotes from the command line above. + +Note that the &W0 command was not used here since the modem will be +initialized each time agetty starts. + +With a V.34 (28.8 kbps) modem try starting with a command like: + + /sbin/agetty -w -I 'ATE0Q1&D2&C1S0=1\015' 115200 ttyS1 + +Note that agetty supports the higher (>9600 bps) serial speeds +directly, there's no need to use setserial to use the higher speeds. diff --git a/term-utils/agetty.8 b/term-utils/agetty.8 new file mode 100644 index 000000000..a7f3bd054 --- /dev/null +++ b/term-utils/agetty.8 @@ -0,0 +1,307 @@ +.TH AGETTY 8 +.SH NAME +agetty \- alternative Linux getty + +.SH SYNOPSIS +.BR "agetty " [\-c8ihLmnsUw] +.RI "[-f " issue_file ] +.RI "[-l " login_program ] +.RI "[-I " init ] +.RI "[-t " timeout ] +.RI "[-H " login_host ] +.I port +.I baud_rate,... +.RI [ term ] + +.SH DESCRIPTION +.ad +.fi +\fBagetty\fP opens a tty port, prompts for a login name and invokes +the /bin/login command. It is normally invoked by \fIinit(8)\fP. + +\fBagetty\fP has several \fInon-standard\fP features that are useful +for hard-wired and for dial-in lines: +.IP o +Adapts the tty settings to parity bits and to erase, kill, +end-of-line and uppercase characters when it reads a login name. +The program can handle 7-bit characters with even, odd, none or space +parity, and 8-bit characters with no parity. The following special +characters are recognized: @ and Control-U (kill); #, DEL and +back space (erase); carriage return and line feed (end of line). +.IP o +Optionally deduces the baud rate from the CONNECT messages produced by +Hayes(tm)-compatible modems. +.IP o +Optionally does not hang up when it is given an already opened line +(useful for call-back applications). +.IP o +Optionally does not display the contents of the \fI/etc/issue\fP file. +.IP o +Optionally displays an alternative issue file instead of \fI/etc/issue\fP. +.IP o +Optionally does not ask for a login name. +.IP o +Optionally invokes a non-standard login program instead of +\fI/bin/login\fP. +.IP o +Optionally turns on hard-ware flow control +.IP o +Optionally forces the line to be local with no need for carrier detect. +.PP +This program does not use the \fI/etc/gettydefs\fP (System V) or +\fI/etc/gettytab\fP (SunOS 4) files. +.SH ARGUMENTS +.na +.nf +.fi +.ad +.TP +port +A path name relative to the \fI/dev\fP directory. If a "-" is +specified, \fBagetty\fP assumes that its standard input is +already connected to a tty port and that a connection to a +remote user has already been established. +.sp +Under System V, a "-" \fIport\fP argument should be preceded +by a "--". +.TP +baud_rate,... +A comma-separated list of one or more baud rates. Each time +\fBagetty\fP receives a BREAK character it advances through +the list, which is treated as if it were circular. +.sp +Baud rates should be specified in descending order, so that the +null character (Ctrl-@) can also be used for baud rate switching. +.TP +term +The value to be used for the TERM environment variable. This overrides +whatever init(8) may have set, and is inherited by login and the shell. +.SH OPTIONS +.na +.nf +.fi +.ad +.TP +\-c +Don't reset terminal cflags (control modes). See \fItermios(3)\fP for more +details. +.TP +\-8 +Assume that the tty is 8-bit clean, hence disable parity detection. +.TP +\-h +Enable hardware (RTS/CTS) flow control. It is left up to the +application to disable software (XON/XOFF) flow protocol where +appropriate. +.TP +\-i +Do not display the contents of \fI/etc/issue\fP (or other) before writing the +login prompt. Terminals or communications hardware may become confused +when receiving lots of text at the wrong baud rate; dial-up scripts +may fail if the login prompt is preceded by too much text. +.TP +\-f \fIissue_file\fP +Display the contents of \fIissue_file\fP instead of \fI/etc/issue\fP. +This allows custom messages to be displayed on different terminals. +The \-i option will override this option. +.TP +\-I \fIinitstring\fP +Set an initial string to be sent to the tty or modem before sending +anything else. This may be used to initialize a modem. Non printable +characters may be sent by writing their octal code preceded by a +backslash (\\). For example to send a linefeed character (ASCII 10, +octal 012) write \\012. +.PP +.TP +\-l \fIlogin_program\fP +Invoke the specified \fIlogin_program\fP instead of /bin/login. +This allows the use of a non-standard login program (for example, +one that asks for a dial-up password or that uses a different +password file). +.TP +\-H \fIlogin_host\fP +Write the specified \fIlogin_host\fP into the utmp file. (Normally, +no login host is given, since \fBagetty\fP is used for local hardwired +connections and consoles. However, this option can be useful for +identifying terminal concentrators and the like. +.TP +\-m +Try to extract the baud rate the CONNECT status message +produced by Hayes(tm)\-compatible modems. These status +messages are of the form: "<junk><speed><junk>". +\fBagetty\fP assumes that the modem emits its status message at +the same speed as specified with (the first) \fIbaud_rate\fP value +on the command line. +.sp +Since the \fI\-m\fP feature may fail on heavily-loaded systems, +you still should enable BREAK processing by enumerating all +expected baud rates on the command line. +.TP +\-n +Do not prompt the user for a login name. This can be used in +connection with \-l option to invoke a non-standard login process such +as a BBS system. Note that with the \-n option, \fBagetty\fR gets no input from +user who logs in and therefore won't be able to figure out parity, +character size, and newline processing of the connection. It defaults to +space parity, 7 bit characters, and ASCII CR (13) end-of-line character. +Beware that the program that \fBagetty\fR starts (usually /bin/login) +is run as root. +.TP +\-t \fItimeout\fP +Terminate if no user name could be read within \fItimeout\fP +seconds. This option should probably not be used with hard-wired +lines. +.TP +\-L +Force the line to be a local line with no need for carrier detect. This can +be useful when you have a locally attached terminal where the serial line +does not set the carrier detect signal. +.TP +\-s +Try to keep the existing baud rate. The baud rates from +the command line are used when agetty receives a BREAK character. +.TP +\-U +Turn on support for detecting an uppercase only terminal. This setting will +detect a login name containing only capitals as indicating an uppercase +only terminal and turn on some upper to lower case conversions. Note that +this has no support for any unicode characters. +.TP +\-w +Wait for the user or the modem to send a carriage-return or a +linefeed character before sending the \fI/etc/issue\fP (or other) file +and the login prompt. Very useful in connection with the \-I option. +.PP +.SH EXAMPLES +This section shows examples for the process field of an entry in the +\fI/etc/inittab\fP file. You'll have to prepend appropriate values +for the other fields. See \fIinittab(5)\fP for more details. + +For a hard-wired line or a console tty: +.ti +5 +/sbin/agetty 9600 ttyS1 + +For a directly connected terminal without proper carriage detect wiring: +(try this if your terminal just sleeps instead of giving you a password: +prompt.) +.ti +5 +/sbin/agetty \-L 9600 ttyS1 vt100 + +For a old style dial-in line with a 9600/2400/1200 baud modem: +.ti +5 +/sbin/agetty \-mt60 ttyS1 9600,2400,1200 + +For a Hayes modem with a fixed 115200 bps interface to the machine: +(the example init string turns off modem echo and result codes, makes +modem/computer DCD track modem/modem DCD, makes a DTR drop cause a +dis-connection and turn on auto-answer after 1 ring.) +.ti +5 +/sbin/agetty \-w \-I 'ATE0Q1&D2&C1S0=1\\015' 115200 ttyS1 + +.SH ISSUE ESCAPES +The issue-file (\fI/etc/issue\fP or the file set with the \-f option) +may contain certain escape codes to display the system name, date and +time etc. All escape codes consist of a backslash (\\) immediately +followed by one of the letters explained below. + +.TP +b +Insert the baudrate of the current line. +.TP +d +Insert the current date. +.TP +s +Insert the system name, the name of the operating system. +.TP +l +Insert the name of the current tty line. +.TP +m +Insert the architecture identifier of the machine, eg. i486 +.TP +n +Insert the nodename of the machine, also known as the hostname. +.TP +o +Insert the NIS domainname of the machine. +.TP +O +Insert the DNS domainname of the machine. +.TP +r +Insert the release number of the OS, eg. 1.1.9. +.TP +t +Insert the current time. +.TP +u +Insert the number of current users logged in. +.TP +U +Insert the string "1 user" or "<n> users" where <n> is the number of current +users logged in. +.TP +v +Insert the version of the OS, eg. the build-date etc. +.TP +Example: On my system, the following \fI/etc/issue\fP file: + +.na +.nf +.ti +.5 +This is \\n.\\o (\\s \\m \\r) \\t +.TP +displays as + +.ti +.5 +This is thingol.orcan.dk (Linux i386 1.1.9) 18:29:30 + +.fi + +.SH FILES +.na +.nf +/var/run/utmp, the system status file. +/etc/issue, printed before the login prompt. +/dev/console, problem reports (if syslog(3) is not used). +/etc/inittab, \fIinit\fP(8) configuration file. +.SH BUGS +.ad +.fi +The baud-rate detection feature (the \fI-m\fP option) requires that +\fBagetty\fP be scheduled soon enough after completion of a dial-in +call (within 30 ms with modems that talk at 2400 baud). For robustness, +always use the \fI\-m\fP option in combination with a multiple baud +rate command-line argument, so that BREAK processing is enabled. + +The text in the \fI/etc/issue\fP file (or other) and the login prompt +are always output with 7-bit characters and space parity. + +The baud-rate detection feature (the \fI-m\fP option) requires that +the modem emits its status message \fIafter\fP raising the DCD line. +.SH DIAGNOSTICS +.ad +.fi +Depending on how the program was configured, all diagnostics are +written to the console device or reported via the syslog(3) facility. +Error messages are produced if the \fIport\fP argument does not +specify a terminal device; if there is no utmp entry for the +current process (System V only); and so on. +.SH AUTHOR(S) +.na +.nf +W.Z. Venema <wietse@wzv.win.tue.nl> +Eindhoven University of Technology +Department of Mathematics and Computer Science +Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands + +Peter Orbaek <poe@daimi.aau.dk> +Linux port and more options. Still maintains the code. + +Eric Rasmussen <ear@usfirst.org> +Added \-f option to display custom login messages on different terminals. + +.SH AVAILABILITY +The agetty command is part of the util-linux package and is available from +ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/term-utils/agetty.c b/term-utils/agetty.c new file mode 100644 index 000000000..57d198039 --- /dev/null +++ b/term-utils/agetty.c @@ -0,0 +1,1306 @@ +/* agetty.c - another getty program for Linux. By W. Z. Venema 1989 + Ported to Linux by Peter Orbaek <poe@daimi.aau.dk> + This program is freely distributable. The entire man-page used to + be here. Now read the real man-page agetty.8 instead. + + -f option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95 + + 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL> + - added Native Language Support + + 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net> + - enable hardware flow control before displaying /etc/issue + +*/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdarg.h> +#include <ctype.h> +#include <utmp.h> +#include <getopt.h> +#include <time.h> +#include <sys/file.h> +#include <sys/socket.h> +#include <netdb.h> + +#include "strutils.h" +#include "nls.h" +#include "pathnames.h" +#include "c.h" + +#ifdef __linux__ +#include <sys/param.h> +#define USE_SYSLOG +#endif + + /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */ + +#ifdef USE_SYSLOG +#include <syslog.h> +#endif + + /* + * Some heuristics to find out what environment we are in: if it is not + * System V, assume it is SunOS 4. + */ + +#ifdef LOGIN_PROCESS /* defined in System V utmp.h */ +#define SYSV_STYLE /* select System V style getty */ +#endif + + /* + * Things you may want to modify. + * + * If ISSUE is not defined, agetty will never display the contents of the + * /etc/issue file. You will not want to spit out large "issue" files at the + * wrong baud rate. Relevant for System V only. + * + * You may disagree with the default line-editing etc. characters defined + * below. Note, however, that DEL cannot be used for interrupt generation + * and for line editing at the same time. + */ + +#ifdef SYSV_STYLE +#define ISSUE "/etc/issue" /* displayed before the login prompt */ +#include <sys/utsname.h> +#endif + +#define LOGIN " login: " /* login prompt */ + +/* Some shorthands for control characters. */ + +#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */ +#define CR CTL('M') /* carriage return */ +#define NL CTL('J') /* line feed */ +#define BS CTL('H') /* back space */ +#define DEL CTL('?') /* delete */ + +/* Defaults for line-editing etc. characters; you may want to change this. */ + +#define DEF_ERASE DEL /* default erase character */ +#define DEF_INTR CTL('C') /* default interrupt character */ +#define DEF_QUIT CTL('\\') /* default quit char */ +#define DEF_KILL CTL('U') /* default kill char */ +#define DEF_EOF CTL('D') /* default EOF char */ +#define DEF_EOL 0 +#define DEF_SWITCH 0 /* default switch char */ + +#ifndef MAXHOSTNAMELEN +# ifdef HOST_NAME_MAX +# define MAXHOSTNAMELEN HOST_NAME_MAX +# else +# define MAXHOSTNAMELEN 64 +# endif +#endif + + /* + * When multiple baud rates are specified on the command line, the first one + * we will try is the first one specified. + */ + +#define FIRST_SPEED 0 + +/* Storage for command-line options. */ + +#define MAX_SPEED 10 /* max. nr. of baud rates */ + +struct options { + int flags; /* toggle switches, see below */ + int timeout; /* time-out period */ + char *login; /* login program */ + char *tty; /* name of tty */ + char *initstring; /* modem init string */ + char *issue; /* alternative issue file */ + int numspeed; /* number of baud rates to try */ + int speeds[MAX_SPEED]; /* baud rates to be tried */ + int eightbits; /* assume 8bit-clean tty */ +}; + +#define F_PARSE (1<<0) /* process modem status messages */ +#define F_ISSUE (1<<1) /* display /etc/issue */ +#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */ +#define F_LOCAL (1<<3) /* force local */ +#define F_INITSTRING (1<<4) /* initstring is set */ +#define F_WAITCRLF (1<<5) /* wait for CR or LF */ +#define F_CUSTISSUE (1<<6) /* give alternative issue file */ +#define F_NOPROMPT (1<<7) /* don't ask for login name! */ +#define F_LCUC (1<<8) /* Support for *LCUC stty modes */ +#define F_KEEPSPEED (1<<9) /* Follow baud rate from kernel */ +#define F_KEEPCFLAGS (1<<10) /* Reuse c_cflags setup from kernel */ + +/* Storage for things detected while the login name was read. */ + +struct chardata { + int erase; /* erase character */ + int kill; /* kill character */ + int eol; /* end-of-line character */ + int parity; /* what parity did we see */ + int capslock; /* upper case without lower case */ +}; + +/* Initial values for the above. */ + +struct chardata init_chardata = { + DEF_ERASE, /* default erase character */ + DEF_KILL, /* default kill character */ + 13, /* default eol char */ + 0, /* space parity */ + 0, /* no capslock */ +}; + +struct Speedtab { + long speed; + int code; +}; + +static struct Speedtab speedtab[] = { + { 50, B50 }, + { 75, B75 }, + { 110, B110 }, + { 134, B134 }, + { 150, B150 }, + { 200, B200 }, + { 300, B300 }, + { 600, B600 }, + { 1200, B1200 }, + { 1800, B1800 }, + { 2400, B2400 }, + { 4800, B4800 }, + { 9600, B9600 }, +#ifdef B19200 + { 19200, B19200 }, +#endif +#ifdef B38400 + { 38400, B38400 }, +#endif +#ifdef EXTA + { 19200, EXTA }, +#endif +#ifdef EXTB + { 38400, EXTB }, +#endif +#ifdef B57600 + { 57600, B57600 }, +#endif +#ifdef B115200 + { 115200, B115200 }, +#endif +#ifdef B230400 + { 230400, B230400 }, +#endif + { 0, 0 }, +}; + +#define P_(s) s +int main P_((int argc, char **argv)); +void parse_args P_((int argc, char **argv, struct options *op)); +void parse_speeds P_((struct options *op, char *arg)); +void update_utmp P_((char *line)); +void open_tty P_((char *tty, struct termios *tp, int local)); +void termio_init P_((struct termios *tp, struct options *op)); +void auto_baud P_((struct termios *tp)); +void do_prompt P_((struct options *op, struct termios *tp)); +void next_speed P_((struct termios *tp, struct options *op)); +char *get_logname P_((struct options *op, struct chardata *cp, struct termios *tp)); +void termio_final P_((struct options *op, struct termios *tp, struct chardata *cp)); +int caps_lock P_((char *s)); +int bcode P_((char *s)); +void usage P_((void)); +void error P_((const char *, ...)); +#undef P_ + +/* The following is used for understandable diagnostics. */ + +char *progname; + +/* Fake hostname for ut_host specified on command line. */ +char *fakehost = NULL; + +/* ... */ +#ifdef DEBUGGING +#define debug(s) fprintf(dbf,s); fflush(dbf) +FILE *dbf; +#else +#define debug(s) /* nothing */ +#endif + +int +main(argc, argv) + int argc; + char **argv; +{ + char *logname = NULL; /* login name, given to /bin/login */ + struct chardata chardata; /* set by get_logname() */ + struct termios termios; /* terminal mode bits */ + static struct options options = { + F_ISSUE, /* show /etc/issue (SYSV_STYLE) */ + 0, /* no timeout */ + _PATH_LOGIN, /* default login program */ + "tty1", /* default tty line */ + "", /* modem init string */ + ISSUE, /* default issue file */ + 0, /* no baud rates known yet */ + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + /* The BSD-style init command passes us a useless process name. */ + +#ifdef SYSV_STYLE + { + char *ptr; + progname = argv[0]; + if ((ptr = strrchr(argv[0], '/'))) + progname = ++ptr; + } +#else + progname = "agetty"; +#endif + +#ifdef DEBUGGING + dbf = fopen("/dev/ttyp0", "w"); + + { int i; + + for(i = 1; i < argc; i++) { + debug(argv[i]); + } + } +#endif + + /* Parse command-line arguments. */ + + parse_args(argc, argv, &options); + +#ifdef __linux__ + setsid(); +#endif + + /* Update the utmp file. */ + +#ifdef SYSV_STYLE + update_utmp(options.tty); +#endif + + debug("calling open_tty\n"); + /* Open the tty as standard { input, output, error }. */ + open_tty(options.tty, &termios, options.flags & F_LOCAL); + + tcsetpgrp(0, getpid()); + /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ + debug("calling termio_init\n"); + termio_init(&termios, &options); + + /* write the modem init string and DON'T flush the buffers */ + if (options.flags & F_INITSTRING) { + debug("writing init string\n"); + ignore_result( write(1, options.initstring, strlen(options.initstring)) ); + } + + if (!(options.flags & F_LOCAL)) { + /* go to blocking write mode unless -L is specified */ + fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK); + } + + /* Optionally detect the baud rate from the modem status message. */ + debug("before autobaud\n"); + if (options.flags & F_PARSE) + auto_baud(&termios); + + /* Set the optional timer. */ + if (options.timeout) + (void) alarm((unsigned) options.timeout); + + /* optionally wait for CR or LF before writing /etc/issue */ + if (options.flags & F_WAITCRLF) { + char ch; + + debug("waiting for cr-lf\n"); + while(read(0, &ch, 1) == 1) { + ch &= 0x7f; /* strip "parity bit" */ +#ifdef DEBUGGING + fprintf(dbf, "read %c\n", ch); +#endif + if (ch == '\n' || ch == '\r') break; + } + } + + chardata = init_chardata; + if (!(options.flags & F_NOPROMPT)) { + /* Read the login name. */ + debug("reading login name\n"); + while ((logname = get_logname(&options, &chardata, &termios)) == 0) + next_speed(&termios, &options); + } + + /* Disable timer. */ + + if (options.timeout) + (void) alarm(0); + + /* Finalize the termios settings. */ + + termio_final(&options, &termios, &chardata); + + /* Now the newline character should be properly written. */ + + ignore_result( write(1, "\n", 1) ); + + /* Let the login program take care of password validation. */ + + (void) execl(options.login, options.login, "--", logname, NULL); + error(_("%s: can't exec %s: %m"), options.tty, options.login); +} + +/* parse-args - parse command-line arguments */ + +void +parse_args(argc, argv, op) + int argc; + char **argv; + struct options *op; +{ + extern char *optarg; /* getopt */ + extern int optind; /* getopt */ + int c; + + while (isascii(c = getopt(argc, argv, "8cI:LH:f:hil:mst:wUn"))) { + switch (c) { + case 'c': + op->flags |= F_KEEPCFLAGS; + break; + case '8': + op->eightbits = 1; + break; + case 'I': + if (!(op->initstring = malloc(strlen(optarg)+1))) { + error(_("can't malloc initstring")); + break; + } + { + char ch, *p, *q; + int i; + + /* copy optarg into op->initstring decoding \ddd + octal codes into chars */ + q = op->initstring; + p = optarg; + while (*p) { + if (*p == '\\') { /* know \\ means \ */ + p++; + if (*p == '\\') { + ch = '\\'; + p++; + } else { /* handle \000 - \177 */ + ch = 0; + for (i = 1; i <= 3; i++) { + if (*p >= '0' && *p <= '7') { + ch <<= 3; + ch += *p - '0'; + p++; + } else + break; + } + } + *q++ = ch; + } else { + *q++ = *p++; + } + } + *q = '\0'; + } + op->flags |= F_INITSTRING; + break; + + case 'L': /* force local */ + op->flags |= F_LOCAL; + break; + case 'H': /* fake login host */ + fakehost = optarg; + break; + case 'f': /* custom issue file */ + op->flags |= F_CUSTISSUE; + op->issue = optarg; + break; + case 'h': /* enable h/w flow control */ + op->flags |= F_RTSCTS; + break; + case 'i': /* do not show /etc/issue */ + op->flags &= ~F_ISSUE; + break; + case 'l': + op->login = optarg; /* non-default login program */ + break; + case 'm': /* parse modem status message */ + op->flags |= F_PARSE; + break; + case 'n': + op->flags |= F_NOPROMPT; + break; + case 's': + op->flags |= F_KEEPSPEED; /* keep kernel defined speed */ + break; + case 't': /* time out */ + if ((op->timeout = atoi(optarg)) <= 0) + error(_("bad timeout value: %s"), optarg); + break; + case 'w': + op->flags |= F_WAITCRLF; + break; + case 'U': + op->flags |= F_LCUC; + break; + default: + usage(); + } + } + debug("after getopt loop\n"); + if (argc < optind + 2) /* check parameter count */ + usage(); + + /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ + if('0' <= argv[optind][0] && argv[optind][0] <= '9') { + /* a number first, assume it's a speed (BSD style) */ + parse_speeds(op, argv[optind++]); /* baud rate(s) */ + op->tty = argv[optind]; /* tty name */ + } else { + op->tty = argv[optind++]; /* tty name */ + parse_speeds(op, argv[optind]); /* baud rate(s) */ + } + + optind++; + if (argc > optind && argv[optind]) + setenv ("TERM", argv[optind], 1); + +#ifdef DO_DEVFS_FIDDLING + /* + * some devfs junk, following Goswin Brederlow: + * turn ttyS<n> into tts/<n> + * turn tty<n> into vc/<n> + */ + if (op->tty && strlen(op->tty) < 90) { + char dev_name[100]; + struct stat st; + + if (strncmp(op->tty, "ttyS", 4) == 0) { + strcpy(dev_name, "/dev/"); + strcat(dev_name, op->tty); + if (stat(dev_name, &st) < 0) { + strcpy(dev_name, "/dev/tts/"); + strcat(dev_name, op->tty + 4); + if (stat(dev_name, &st) == 0) + op->tty = strdup(dev_name + 5); + } + } else if (strncmp(op->tty, "tty", 3) == 0) { + strcpy(dev_name, "/dev/"); + strncat(dev_name, op->tty, 90); + if (stat(dev_name, &st) < 0) { + strcpy(dev_name, "/dev/vc/"); + strcat(dev_name, op->tty + 3); + if (stat(dev_name, &st) == 0) + op->tty = strdup(dev_name + 5); + } + } + } +#endif + + debug("exiting parseargs\n"); +} + +/* parse_speeds - parse alternate baud rates */ + +void +parse_speeds(op, arg) + struct options *op; + char *arg; +{ + char *cp; + + debug("entered parse_speeds\n"); + for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) { + if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) + error(_("bad speed: %s"), cp); + if (op->numspeed >= MAX_SPEED) + error(_("too many alternate speeds")); + } + debug("exiting parsespeeds\n"); +} + +#ifdef SYSV_STYLE + +/* update_utmp - update our utmp entry */ +void +update_utmp(line) + char *line; +{ + struct utmp ut; + time_t t; + int mypid = getpid(); + struct utmp *utp; + + /* + * The utmp file holds miscellaneous information about things started by + * /sbin/init and other system-related events. Our purpose is to update + * the utmp entry for the current process, in particular the process type + * and the tty line we are listening to. Return successfully only if the + * utmp file can be opened for update, and if we are able to find our + * entry in the utmp file. + */ + + utmpname(_PATH_UTMP); + setutent(); + + /* Find mypid in utmp. Earlier code here tested only + utp->ut_type != INIT_PROCESS, so maybe the >= here should be >. + The present code is taken from login.c, so if this is changed, + maybe login has to be changed as well. */ + while ((utp = getutent())) + if (utp->ut_pid == mypid + && utp->ut_type >= INIT_PROCESS + && utp->ut_type <= DEAD_PROCESS) + break; + + if (utp) { + memcpy(&ut, utp, sizeof(ut)); + } else { + /* some inits don't initialize utmp... */ + memset(&ut, 0, sizeof(ut)); + strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); + } + + strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user)); + strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + if (fakehost) + strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); + time(&t); + ut.ut_time = t; + ut.ut_type = LOGIN_PROCESS; + ut.ut_pid = mypid; + + pututline(&ut); + endutent(); + + { +#ifdef HAVE_UPDWTMP + updwtmp(_PATH_WTMP, &ut); +#else + int ut_fd; + int lf; + + if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) { + flock(lf, LOCK_EX); + if ((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) { + ignore_result( write(ut_fd, &ut, sizeof(ut)) ); + close(ut_fd); + } + flock(lf, LOCK_UN); + close(lf); + } +#endif + } +} + +#endif + +/* open_tty - set up tty as standard { input, output, error } */ +void +open_tty(tty, tp, local) + char *tty; + struct termios *tp; + int local; +{ + /* Get rid of the present standard { output, error} if any. */ + + (void) close(1); + (void) close(2); + errno = 0; /* ignore above errors */ + + /* Set up new standard input, unless we are given an already opened port. */ + + if (strcmp(tty, "-")) { + struct stat st; + + /* Sanity checks... */ + + if (chdir("/dev")) + error(_("/dev: chdir() failed: %m")); + if (stat(tty, &st) < 0) + error("/dev/%s: %m", tty); + if ((st.st_mode & S_IFMT) != S_IFCHR) + error(_("/dev/%s: not a character device"), tty); + + /* Open the tty as standard input. */ + + (void) close(0); + errno = 0; /* ignore close(2) errors */ + + debug("open(2)\n"); + if (open(tty, O_RDWR|O_NONBLOCK, 0) != 0) + error(_("/dev/%s: cannot open as standard input: %m"), tty); + + } else { + + /* + * Standard input should already be connected to an open port. Make + * sure it is open for read/write. + */ + + if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR) + error(_("%s: not open for read/write"), tty); + } + + /* Set up standard output and standard error file descriptors. */ + debug("duping\n"); + if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */ + error(_("%s: dup problem: %m"), tty); /* we have a problem */ + + /* + * The following ioctl will fail if stdin is not a tty, but also when + * there is noise on the modem control lines. In the latter case, the + * common course of action is (1) fix your cables (2) give the modem more + * time to properly reset after hanging up. SunOS users can achieve (2) + * by patching the SunOS kernel variable "zsadtrlow" to a larger value; + * 5 seconds seems to be a good value. + */ + + if (tcgetattr(0, tp) < 0) + error("%s: tcgetattr: %m", tty); + + /* + * It seems to be a terminal. Set proper protections and ownership. Mode + * 0622 is suitable for SYSV <4 because /bin/login does not change + * protections. SunOS 4 login will change the protections to 0620 (write + * access for group tty) after the login has succeeded. + * + * Linux login(1) will change tty permissions. + */ + + /* + * Let us use 0600 for Linux for the period between getty and login + */ + ignore_result( chown(tty, 0, 0) ); /* root, sys */ + ignore_result( chmod(tty, 0600) ); /* 0622: crw--w--w- */ + errno = 0; /* ignore above errors */ +} + +/* termio_init - initialize termios settings */ + +char gbuf[1024]; +char area[1024]; + +void +termio_init(tp, op) + struct termios *tp; + struct options *op; +{ + speed_t ispeed, ospeed; + + if (op->flags & F_KEEPSPEED) { + ispeed = cfgetispeed(tp); /* save the original setting */ + ospeed = cfgetospeed(tp); + } else + ospeed = ispeed = op->speeds[FIRST_SPEED]; + + /* + * Initial termios settings: 8-bit characters, raw-mode, blocking i/o. + * Special characters are set after we have read the login name; all + * reads will be done in raw mode anyway. Errors will be dealt with + * lateron. + */ + /* flush input and output queues, important for modems! */ + (void) tcflush(0, TCIOFLUSH); + + tp->c_iflag = tp->c_lflag = tp->c_oflag = 0; + + if (!(op->flags & F_KEEPCFLAGS)) + tp->c_cflag = CS8 | HUPCL | CREAD | (tp->c_cflag & CLOCAL); + + /* Note that the speed is stored in the c_cflag termios field, so we have + * set the speed always when the cflag se reseted. + */ + cfsetispeed(tp, ispeed); + cfsetospeed(tp, ospeed); + + if (op->flags & F_LOCAL) { + tp->c_cflag |= CLOCAL; + } + +#ifdef HAVE_STRUCT_TERMIOS_C_LINE + tp->c_line = 0; +#endif + tp->c_cc[VMIN] = 1; + tp->c_cc[VTIME] = 0; + + /* Optionally enable hardware flow control */ + +#ifdef CRTSCTS + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; +#endif + + (void) tcsetattr(0, TCSANOW, tp); + + /* go to blocking input even in local mode */ + fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK); + + debug("term_io 2\n"); +} + +/* auto_baud - extract baud rate from modem status message */ +void +auto_baud(tp) + struct termios *tp; +{ + int speed; + int vmin; + unsigned iflag; + char buf[BUFSIZ]; + char *bp; + int nread; + + /* + * This works only if the modem produces its status code AFTER raising + * the DCD line, and if the computer is fast enough to set the proper + * baud rate before the message has gone by. We expect a message of the + * following format: + * + * <junk><number><junk> + * + * The number is interpreted as the baud rate of the incoming call. If the + * modem does not tell us the baud rate within one second, we will keep + * using the current baud rate. It is advisable to enable BREAK + * processing (comma-separated list of baud rates) if the processing of + * modem status messages is enabled. + */ + + /* + * Use 7-bit characters, don't block if input queue is empty. Errors will + * be dealt with lateron. + */ + + iflag = tp->c_iflag; + tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ + vmin = tp->c_cc[VMIN]; + tp->c_cc[VMIN] = 0; /* don't block if queue empty */ + tcsetattr(0, TCSANOW, tp); + + /* + * Wait for a while, then read everything the modem has said so far and + * try to extract the speed of the dial-in call. + */ + + (void) sleep(1); + if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) { + buf[nread] = '\0'; + for (bp = buf; bp < buf + nread; bp++) { + if (isascii(*bp) && isdigit(*bp)) { + if ((speed = bcode(bp))) { + cfsetispeed(tp, speed); + cfsetospeed(tp, speed); + } + break; + } + } + } + /* Restore terminal settings. Errors will be dealt with lateron. */ + + tp->c_iflag = iflag; + tp->c_cc[VMIN] = vmin; + (void) tcsetattr(0, TCSANOW, tp); +} + +/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ +void +do_prompt(op, tp) + struct options *op; + struct termios *tp; +{ +#ifdef ISSUE + FILE *fd; + int oflag; + int c; + struct utsname uts; + + (void) uname(&uts); +#endif + + ignore_result( write(1, "\r\n", 2) ); /* start a new line */ +#ifdef ISSUE /* optional: show /etc/issue */ + if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) { + oflag = tp->c_oflag; /* save current setting */ + tp->c_oflag |= (ONLCR | OPOST); /* map NL in output to CR-NL */ + (void) tcsetattr(0, TCSADRAIN, tp); + + + while ((c = getc(fd)) != EOF) + { + if (c == '\\') + { + c = getc(fd); + + switch (c) + { + case 's': + (void) printf ("%s", uts.sysname); + break; + + case 'n': + (void) printf ("%s", uts.nodename); + break; + + case 'r': + (void) printf ("%s", uts.release); + break; + + case 'v': + (void) printf ("%s", uts.version); + break; + + case 'm': + (void) printf ("%s", uts.machine); + break; + + case 'o': + { + char domainname[MAXHOSTNAMELEN+1]; +#ifdef HAVE_GETDOMAINNAME + if (getdomainname(domainname, sizeof(domainname))) +#endif + strcpy(domainname, "unknown_domain"); + domainname[sizeof(domainname)-1] = '\0'; + printf ("%s", domainname); + } + break; + + case 'O': + { + char *dom = "unknown_domain"; + char host[MAXHOSTNAMELEN+1]; + struct addrinfo hints, *info = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + + if (gethostname(host, sizeof(host)) || + getaddrinfo(host, NULL, &hints, &info) || + info == NULL) + fputs(dom, stdout); + else { + char *canon; + + if (info->ai_canonname && + (canon = strchr(info->ai_canonname, '.'))) + dom = canon + 1; + fputs(dom, stdout); + freeaddrinfo(info); + } + } + break; + + case 'd': + case 't': + { + time_t now; + struct tm *tm; + + (void) time (&now); + tm = localtime(&now); + + if (c == 'd') + (void) printf ("%s %s %d %d", + nl_langinfo(ABDAY_1 + tm->tm_wday), + nl_langinfo(ABMON_1 + tm->tm_mon), + tm->tm_mday, + tm->tm_year < 70 ? tm->tm_year + 2000 : + tm->tm_year + 1900); + else + (void) printf ("%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + break; + } + + case 'l': + (void) printf ("%s", op->tty); + break; + + case 'b': + { + int i; + + for (i = 0; speedtab[i].speed; i++) { + if (speedtab[i].code == cfgetispeed(tp)) { + printf("%ld", speedtab[i].speed); + break; + } + } + break; + } + case 'u': + case 'U': + { + int users = 0; + struct utmp *ut; + setutent(); + while ((ut = getutent())) + if (ut->ut_type == USER_PROCESS) + users++; + endutent(); + printf ("%d ", users); + if (c == 'U') + printf ((users == 1) ? _("user") : _("users")); + break; + } + default: + (void) putchar(c); + } + } + else + (void) putchar(c); + } + fflush(stdout); + + tp->c_oflag = oflag; /* restore settings */ + (void) tcsetattr(0, TCSADRAIN, tp); /* wait till output is gone */ + (void) fclose(fd); + } +#endif + { + char hn[MAXHOSTNAMELEN+1]; + if (gethostname(hn, sizeof(hn)) == 0) + ignore_result( write(1, hn, strlen(hn)) ); + } + ignore_result( write(1, LOGIN, sizeof(LOGIN) - 1) ); /* always show login prompt */ +} + +/* next_speed - select next baud rate */ +void +next_speed(tp, op) + struct termios *tp; + struct options *op; +{ + static int baud_index = -1; + + if (baud_index == -1) + /* + * if the F_KEEPSPEED flags is set then the FIRST_SPEED is not + * tested yet (see termio_init()). + */ + baud_index = (op->flags & F_KEEPSPEED) ? FIRST_SPEED : + 1 % op->numspeed; + else + baud_index = (baud_index + 1) % op->numspeed; + + cfsetispeed(tp, op->speeds[baud_index]); + cfsetospeed(tp, op->speeds[baud_index]); + (void) tcsetattr(0, TCSANOW, tp); +} + +/* get_logname - get user name, establish parity, speed, erase, kill, eol */ + +char *get_logname(op, cp, tp) + struct options *op; + struct chardata *cp; + struct termios *tp; +{ + static char logname[BUFSIZ]; + char *bp; + char c; /* input character, full eight bits */ + char ascval; /* low 7 bits of input character */ + int bits; /* # of "1" bits per character */ + int mask; /* mask with 1 bit up */ + static char *erase[] = { /* backspace-space-backspace */ + "\010\040\010", /* space parity */ + "\010\040\010", /* odd parity */ + "\210\240\210", /* even parity */ + "\210\240\210", /* no parity */ + }; + + /* Initialize kill, erase, parity etc. (also after switching speeds). */ + + *cp = init_chardata; + + /* Flush pending input (esp. after parsing or switching the baud rate). */ + + (void) sleep(1); + (void) tcflush(0, TCIFLUSH); + + /* Prompt for and read a login name. */ + + for (*logname = 0; *logname == 0; /* void */ ) { + + /* Write issue file and prompt, with "parity" bit == 0. */ + + do_prompt(op, tp); + + /* Read name, watch for break, parity, erase, kill, end-of-line. */ + + for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) { + + /* Do not report trivial EINTR/EIO errors. */ + + if (read(0, &c, 1) < 1) { + if (errno == EINTR || errno == EIO) + exit(EXIT_SUCCESS); + error(_("%s: read: %m"), op->tty); + } + /* Do BREAK handling elsewhere. */ + + if ((c == 0) && op->numspeed > 1) + return EXIT_SUCCESS; + /* Do parity bit handling. */ + + if (op->eightbits) { + ascval = c; + } else if (c != (ascval = (c & 0177))) { /* "parity" bit on */ + for (bits = 1, mask = 1; mask & 0177; mask <<= 1) + if (mask & ascval) + bits++; /* count "1" bits */ + cp->parity |= ((bits & 1) ? 1 : 2); + } + /* Do erase, kill and end-of-line processing. */ + + switch (ascval) { + case CR: + case NL: + *bp = 0; /* terminate logname */ + cp->eol = ascval; /* set end-of-line char */ + break; + case BS: + case DEL: + case '#': + cp->erase = ascval; /* set erase character */ + if (bp > logname) { + ignore_result( write(1, erase[cp->parity], 3) ); + bp--; + } + break; + case CTL('U'): + case '@': + cp->kill = ascval; /* set kill character */ + while (bp > logname) { + ignore_result( write(1, erase[cp->parity], 3) ); + bp--; + } + break; + case CTL('D'): + exit(EXIT_SUCCESS); + default: + if (!isascii(ascval) || !isprint(ascval)) { + /* ignore garbage characters */ ; + } else if (bp - logname >= sizeof(logname) - 1) { + error(_("%s: input overrun"), op->tty); + } else { + ignore_result( write(1, &c, 1) ); /* echo the character */ + *bp++ = ascval; /* and store it */ + } + break; + } + } + } + /* Handle names with upper case and no lower case. */ + if ((op->flags & F_LCUC) && (cp->capslock = caps_lock(logname))) { + for (bp = logname; *bp; bp++) + if (isupper(*bp)) + *bp = tolower(*bp); /* map name to lower case */ + } + return logname; +} + +/* termio_final - set the final tty mode bits */ +void +termio_final(op, tp, cp) + struct options *op; + struct termios *tp; + struct chardata *cp; +{ + /* General terminal-independent stuff. */ + + tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */ + tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK| ECHOKE; + /* no longer| ECHOCTL | ECHOPRT*/ + tp->c_oflag |= OPOST; + /* tp->c_cflag = 0; */ + tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */ + tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */ + tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */ + tp->c_cc[VEOL] = DEF_EOL; +#ifdef __linux__ + tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ +#elif defined(VSWTCH) + tp->c_cc[VSWTCH] = DEF_SWITCH; /* default switch character */ +#endif + + /* Account for special characters seen in input. */ + + if (cp->eol == CR) { + tp->c_iflag |= ICRNL; /* map CR in input to NL */ + tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */ + } + tp->c_cc[VERASE] = cp->erase; /* set erase character */ + tp->c_cc[VKILL] = cp->kill; /* set kill character */ + + /* Account for the presence or absence of parity bits in input. */ + + switch (cp->parity) { + case 0: /* space (always 0) parity */ + break; + case 1: /* odd parity */ + tp->c_cflag |= PARODD; + /* FALLTHROUGH */ + case 2: /* even parity */ + tp->c_cflag |= PARENB; + tp->c_iflag |= INPCK | ISTRIP; + /* FALLTHROUGH */ + case (1 | 2): /* no parity bit */ + tp->c_cflag &= ~CSIZE; + tp->c_cflag |= CS7; + break; + } + /* Account for upper case without lower case. */ + + if (cp->capslock) { +#ifdef IUCLC + tp->c_iflag |= IUCLC; +#endif +#ifdef XCASE + tp->c_lflag |= XCASE; +#endif +#ifdef OLCUC + tp->c_oflag |= OLCUC; +#endif + } + /* Optionally enable hardware flow control */ + +#ifdef CRTSCTS + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; +#endif + + /* Finally, make the new settings effective */ + + if (tcsetattr(0, TCSANOW, tp) < 0) + error("%s: tcsetattr: TCSANOW: %m", op->tty); +} + +/* caps_lock - string contains upper case without lower case */ +int +caps_lock(s) + char *s; +{ + int capslock; + + for (capslock = 0; *s; s++) { + if (islower(*s)) + return EXIT_SUCCESS; + if (capslock == 0) + capslock = isupper(*s); + } + return capslock; +} + +/* bcode - convert speed string to speed code; return 0 on failure */ +int +bcode(s) + char *s; +{ + struct Speedtab *sp; + long speed = atol(s); + + for (sp = speedtab; sp->speed; sp++) + if (sp->speed == speed) + return sp->code; + return 0; +} + +/* usage - explain */ + +void __attribute__((__noreturn__)) usage(void) +{ + fprintf(stderr, _("Usage: %s [-8hiLmsUw] [-l login_program] [-t timeout] [-I initstring] [-H login_host] baud_rate,... line [termtype]\nor\t[-hiLmw] [-l login_program] [-t timeout] [-I initstring] [-H login_host] line baud_rate,... [termtype]\n"), progname); + exit(EXIT_FAILURE); +} + +/* error - report errors to console or syslog; only understands %s and %m */ + +#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2) + +void +error(const char *fmt, ...) { + va_list ap; +#ifndef USE_SYSLOG + int fd; +#endif + char buf[BUFSIZ]; + char *bp; + + /* + * If the diagnostic is reported via syslog(3), the process name is + * automatically prepended to the message. If we write directly to + * /dev/console, we must prepend the process name ourselves. + */ + +#ifdef USE_SYSLOG + buf[0] = '\0'; + bp = buf; +#else + (void) str2cpy(buf, progname, ": "); + bp = buf + strlen(buf); +#endif + + /* + * %s expansion is done by hand. On a System V Release 2 system without + * shared libraries and without syslog(3), linking with the the stdio + * library would make the program three times as big... + * + * %m expansion is done here as well. Too bad syslog(3) does not have a + * vsprintf() like interface. + */ + + va_start(ap, fmt); + while (*fmt && bp < &buf[BUFSIZ-1]) { + if (strncmp(fmt, "%s", 2) == 0) { + xstrncpy(bp, va_arg(ap, char *), &buf[BUFSIZ-1] - bp); + bp += strlen(bp); + fmt += 2; + } else if (strncmp(fmt, "%m", 2) == 0) { + xstrncpy(bp, strerror(errno), &buf[BUFSIZ-1] - bp); + bp += strlen(bp); + fmt += 2; + } else { + *bp++ = *fmt++; + } + } + *bp = 0; + va_end(ap); + + /* + * Write the diagnostic directly to /dev/console if we do not use the + * syslog(3) facility. + */ + +#ifdef USE_SYSLOG + (void) openlog(progname, LOG_PID, LOG_AUTHPRIV); + (void) syslog(LOG_ERR, "%s", buf); + closelog(); +#else + /* Terminate with CR-LF since the console mode is unknown. */ + (void) strcat(bp, "\r\n"); + if ((fd = open("/dev/console", 1)) >= 0) { + ignore_result( write(fd, buf, strlen(buf)) ); + (void) close(fd); + } +#endif + (void) sleep((unsigned) 10); /* be kind to init(8) */ + exit(1); +} |