home *** CD-ROM | disk | FTP | other *** search
- /*++
- /* NAME
- /* agetty 8
- /* SUMMARY
- /* alternative System-V getty for dial-up lines
- /* SYNOPSIS
- /* agetty [-a alternate_rates] [-h] [-i] [-m] [-t timeout] port baud_rate
- /* DESCRIPTION
- /* \fIagetty\fR opens a tty port, prompts for a login name and invokes the
- /* /bin/login command. It is normally invoked by \fIinit(8)\fR.
- /*
- /* \fIagetty\fR has some useful features for dial-up lines that are
- /* not present in the System V Release 2 getty command:
- /* .IP o
- /* Adapts the tty settings to parity bits and to
- /* erase, kill and end-of-line characters found in its input. The
- /* program understands 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 recognizes the baud rate of incoming calls from the
- /* status messages produced by some multi-speed Hayes-compatible modems.
- /* .IP o
- /* Optionally does not display the contents of the \fI/etc/issue\fR file.
- /* .PP
- /* This program does not use the \fI/etc/gettydefs\fR file. Except for
- /* differences described in the documentation, the program appears to
- /* operate similar to the System-V Release 2 \fIgetty\fR program.
- /*
- /* Options:
- /* .TP
- /* -a alternate_rates
- /* Initially the program will use the \fIbaud_rate\fR as specified.
- /* Upon receipt of successive BREAK characters the program will step
- /* through the \fIalternate_rates\fR, which should be specified as a
- /* comma-separated list (preferably in decreasing order). After all
- /* \fIalternate_rates\fR have been tried, \fIagetty\fR will try the
- /* speed specified with the \fIbaud_rate\fR argument and so on.
- /* .TP
- /* -h
- /* Do not hang up the line. Normally, \fIagetty\fR will lower
- /* DTR for two seconds to force a modem to hang up (if the hangup
- /* feature has been compiled into the program).
- /* .TP
- /* -i
- /* Do not display the contents of \fI/etc/issue\fR before writing the
- /* login prompt. Terminals or computer programs 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
- /* -m
- /* Try to extract the baud rate of incoming calls from the status message
- /* produced by some multi-speed Hayes-compatible modems. These usually
- /* produce a status message of the form: "<junk><speed><junk>".
- /* If no \fIspeed\fR is found within one second, the \fIbaud_rate\fR as
- /* specified on the command line will be used. Since the \fI-m\fR feature
- /* will work only on lightly-loaded systems, you will probably want to use
- /* it in combination with the \fI-a\fR option.
- /* .TP
- /* -t timeout
- /* Causes the program to terminate if no user name could be read
- /* within \fItimeout\fR seconds. This is useful only for dial-in lines.
- /* EXAMPLES
- /* For hard-wired lines:
- /* .ti +5
- /* /etc/agetty ttyM0 9600
- /*
- /* For dial-in lines with a 300/1200/2400 baud multi-speed modem:
- /* .ti +5
- /* /etc/agetty -t60 -m -a1200,300 ttyM1 2400
- /* FILES
- /* /etc/utmp, the system log file.
- /* /etc/issue, printed before the login prompt.
- /* /dev/console, problem reports.
- /* BUGS
- /* The baud-rate detection code (the \fI-m\fR option) only works if
- /* \fIagetty\fR is 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\fR option in combination with the \fI-a\fR option.
- /*
- /* The contents of the /etc/issue file and the login prompt are always
- /* output with space parity.
- /* DIAGNOSTICS
- /* All diagnostics are written to the console device. Error messages are
- /* produced if the \fIport\fR argument does not specify a terminal; if
- /* there is no /etc/utmp entry for the current process; and so on.
- /* AUTHOR(S)
- /* 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
- /* CREATION DATE
- /* Sat Nov 25 22:51:05 MET 1989
- /* LAST MODIFICATION
- /* 90/01/28 17:53:06
- /* VERSION/RELEASE
- /* 1.26
- /*--*/
-
- #ifndef lint
- static char sccsid[] = "@(#) agetty.c 1.26 1/28/90 17:53:06";
- #endif
-
- #include <termio.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <varargs.h>
- #include <ctype.h>
- #include <utmp.h>
-
- /*
- * Things you may want to modify.
- *
- * HANGUP should be defined only if your tty driver is not able to hang up the
- * modem (by briefly dropping DTR). If HANGUP is defined you probably cannot
- * use the auto-baud and time-out features.
- *
- * 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.
- *
- * 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.
- */
-
- #define ISSUE "/etc/issue" /* shown before login prompt */
-
- #define LOGIN "login: " /* login prompt */
-
- /* #define HANGUP /* enable hangup code */
-
- /* 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_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_SWITCH CTL('^') /* default switch char */
- #define DEF_ERASE BS /* default erase char, see below */
- #define DEF_EOL 0
-
- /*
- * This program does not need the standard-i/o library. This keeps the
- * executable small; useful for systems that do not have shared libaries
- * (Sys-V Rel <3).
- */
-
- #define BUFSIZ 1024
-
- /* Storage for command-line options */
-
- #define MAXSPEED 10
-
- struct options {
- int flags; /* toggle switches, see below */
- int timeout; /* time-out period */
- int numspeed; /* number of baud rates to try */
- int curspeed; /* current speed */
- int speeds[MAXSPEED]; /* baud rates to be tried */
- char *tty; /* name of tty */
- };
-
- #define F_PARSE (1<<0) /* process modem status messages */
- #define F_HANGUP (1<<1) /* hangup line */
- #define F_ISSUE (1<<2) /* display /etc/issue */
-
- /* 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 */
- };
-
- /* The following is used for understandable diagnostics */
-
- extern int errno;
- extern char *sys_errlist[];
- static char *progname;
- extern char *strcpy();
- extern char *strcat();
-
- /* ... */
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- char *logname; /* login name, given to /bin/login */
- char *get_logname();
- struct chardata chardata; /* set by get_logname() */
- struct termio termio; /* terminal mode bits */
- static struct options options = {
- F_HANGUP | F_ISSUE, /* hangup line and show /etc/issue */
- 0, /* no timeout */
- 1, /* no alternate baud rates */
- 0, /* no alternate baud rates */
- };
-
- progname = argv[0];
-
- /* Parse command-line arguments */
-
- parse_args(argc, argv, &options);
-
- /* Update the utmp file */
-
- update_utmp(options.tty);
-
- /* Open the tty as standard { input, output, error } */
-
- open_tty(options.tty, &termio);
-
- /* Optionally hang up the tty */
-
- if (options.flags & F_HANGUP)
- hangup_tty(&termio);
-
- /* Initialize the termio settings (raw mode, eight-bit, blocking i/o) */
-
- termio_init(&termio, options.speeds[0]);
-
- /* Optionally detect the baud rate from the modem status message */
-
- if (options.flags & F_PARSE)
- auto_baud(&termio);
-
- /* With dial-in lines, briefly pause to allow modems etc. to settle */
-
- if (options.timeout)
- (void) sleep(1);
-
- /* Optional time-out feature */
-
- if (options.timeout)
- (void) alarm((unsigned) options.timeout);
-
- /* Read the login name */
-
- while ((logname = get_logname(&options, &chardata, &termio)) == 0)
- next_speed(&termio, &options);
-
- /* Disable time-out feature */
-
- if (options.timeout)
- (void) alarm(0);
-
- /* Finalize the termio settings */
-
- termio_final(&termio, &chardata);
-
- /* Now the newline character should be properly written */
-
- (void) write(1, "\n", 1);
-
- /* Let /bin/login take care of password validation */
-
- (void) execl("/bin/login", "login", logname, (char *) 0);
- error("%s: can't exec /bin/login", options.tty);
- /* NOTREACHED */
- }
-
- /* parse-args - parse command-line arguments */
-
- 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, "a:himt:"))) {
- switch (c) {
- case 'a': /* enable auto-baud feature */
- parse_speeds(op, optarg);
- break;
- case 'h': /* do not hangup the tty */
- op->flags &= ~F_HANGUP;
- break;
- case 'i': /* do not show /etc/issue */
- op->flags &= ~F_ISSUE;
- break;
- case 'm': /* parse modem status message */
- op->flags |= F_PARSE;
- break;
- case 't': /* time out */
- if ((op->timeout = atoi(optarg)) <= 0)
- error("bad timeout value: %s", optarg);
- break;
- case '?':
- usage();
- }
- }
- if (argc != optind + 2) /* check parameter count */
- usage();
- op->tty = argv[optind++]; /* tty name */
- if ((op->speeds[0] = bcode(argv[optind])) <= 0) /* baud rate */
- error("bad speed: %s", argv[optind]);
- }
-
- /* parse_speeds - parse alternate baud rates */
-
- parse_speeds(op, arg)
- struct options *op;
- char *arg;
- {
- char *strtok();
- char *cp;
-
- 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 > MAXSPEED)
- error("too many alternate speeds");
- }
- }
-
- /* update_utmp - update our utmp entry */
-
- update_utmp(line)
- char *line;
- {
- struct utmp ut;
- long ut_size = sizeof(ut); /* avoid nonsense */
- int ut_fd;
- int mypid = getpid();
- long time();
- long lseek();
- char *strncpy();
-
- /*
- * The utmp file holds miscellaneous information about things started by
- * /etc/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.
- */
-
- if ((ut_fd = open(UTMP_FILE, 2)) < 0) {
- error("%s: open for update", UTMP_FILE);
- } else {
- while (read(ut_fd, (char *) &ut, sizeof(ut)) == sizeof(ut)) {
- if (ut.ut_type == INIT_PROCESS && ut.ut_pid == mypid) {
- ut.ut_type = LOGIN_PROCESS;
- ut.ut_time = time((long *) 0);
- (void) strncpy(ut.ut_name, "LOGIN", sizeof(ut.ut_name));
- (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
- (void) lseek(ut_fd, -ut_size, 1);
- (void) write(ut_fd, (char *) &ut, sizeof(ut));
- (void) close(ut_fd);
- return;
- }
- }
- error("no utmp entry found for process id %u", mypid);
- }
- }
-
- /* open_tty - open tty as standard { input, output, error } */
-
- open_tty(tty, tp)
- char *tty;
- struct termio *tp;
- {
- struct stat st;
-
- /* Close standard { input, output, error } files, just in case */
-
- (void) close(0);
- (void) close(1);
- (void) close(2);
- errno = 0; /* ignore above errors */
-
- /* Make sure we are given a character device */
-
- if (chdir("/dev"))
- error("/dev: chdir() failed");
- if (stat(tty, &st) < 0)
- error("/dev/%s: stat() failed", tty);
- if ((st.st_mode & S_IFMT) != S_IFCHR)
- error("not a character device: /dev/%s", tty);
-
- /* Set up new standard input, output and error files */
-
- if (open(tty, 2) != 0) /* set up std input */
- error("/dev/%s: cannot open as standard input", tty);
- if (dup(0) != 1 || dup(0) != 2) /* set up std out and std err */
- error("%s: dup problem", tty); /* we have a problem */
- if (ioctl(0, TCGETA, tp) < 0) /* read tty status bits */
- error("%s: ioctl failed", tty); /* this is not a terminal */
-
- /* It seems to be a terminal; set proper protections and ownership */
-
- (void) chown(tty, 0, 0); /* root, sys */
- (void) chmod(tty, 0622); /* crw--w--w- */
- errno = 0; /* ignore above errors */
- }
-
- #ifdef lint
- #define HANGUP
- #endif
-
- /* hangup_tty - hang up by forcing DTR down for at least 2 seconds */
-
- hangup_tty(tp)
- struct termio *tp;
- {
- #ifdef HANGUP
- (void) signal(SIGHUP, SIG_IGN);
- tp->c_cflag &= ~CBAUD;
- tp->c_cflag |= B0;
- (void) ioctl(0, TCSETA, tp);
- (void) signal(SIGHUP, SIG_DFL);
- (void) sleep(2);
- #endif
- }
-
- /* termio_init - initialize termio settings */
-
- termio_init(tp, speed)
- struct termio *tp;
- int speed;
- {
-
- /*
- * Initial termio 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.
- */
-
- tp->c_cflag = CS8 | HUPCL | CREAD | speed;
- tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
- tp->c_cc[VMIN] = 1;
- tp->c_cc[VTIME] = 0;
- (void) ioctl(0, TCSETA, tp);
- }
-
- /* auto_baud - extract baud rate from modem status message */
-
- auto_baud(tp)
- struct termio *tp;
- {
- int speed;
- int vmin;
- int 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 baud-rate
- * cycling (-a option) if the processing of modem status messages is
- * enabled.
- */
-
- /* Use 7-bit characters, don't block if input queue is empty */
-
- 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 */
- (void) ioctl(0, TCSETA, 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)) {
- tp->c_cflag &= ~CBAUD;
- tp->c_cflag |= speed;
- }
- break;
- }
- }
- }
- /* Restore settings */
-
- tp->c_iflag = iflag;
- tp->c_cc[VMIN] = vmin;
- (void) ioctl(0, TCSETA, tp);
- }
-
- /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
-
- do_prompt(op, tp)
- struct options *op;
- struct termio *tp;
- {
- #ifdef ISSUE
- int fd;
- int oflag;
- int n;
- char buf[BUFSIZ];
- #endif
-
- write(1, "\r\n", 2); /* Start a new line */
- #ifdef ISSUE /* Optional: show /etc/issue */
- if ((op->flags & F_ISSUE) && (fd = open(ISSUE, 0)) >= 0) {
- oflag = tp->c_oflag; /* Save current setting */
- tp->c_oflag |= (ONLCR | OPOST); /* Map NL in output to CR-NL */
- (void) ioctl(0, TCSETAW, tp);
- while ((n = read(fd, buf, sizeof(buf))) > 0)
- (void) write(1, buf, n);
- tp->c_oflag = oflag; /* Restore settings */
- (void) ioctl(0, TCSETAW, tp); /* Wait till output gone */
- (void) close(fd);
- }
- #endif
- (void) write(1, LOGIN, sizeof(LOGIN) - 1); /* Always show login prompt */
- }
-
- /* next_speed - select next baud rate */
-
- next_speed(tp, op)
- struct termio *tp;
- struct options *op;
- {
- op->curspeed = (op->curspeed + 1) % op->numspeed;
- tp->c_cflag &= ~CBAUD;
- tp->c_cflag |= op->speeds[op->curspeed];
- (void) ioctl(0, TCSETA, 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 termio *tp;
- {
- 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 etcetera (also after switching speeds) */
-
- cp->kill = DEF_KILL;
- cp->erase = DEF_ERASE;
- cp->parity = 0;
-
- /* Flush any pending input */
-
- (void) ioctl(0, TCFLSH, (struct termio *) 0);
-
- /* 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 */ ) {
- if (read(0, &c, 1) < 1)
- error("%s: read error", op->tty);
-
- /* Do BREAK handling elsewhere */
-
- if ((c == 0) && op->numspeed > 1)
- return (0);
-
- /* Do parity bit handling */
-
- 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) {
- (void) write(1, erase[cp->parity], 3);
- bp--;
- }
- break;
- case CTL('U'):
- case '@':
- cp->kill = ascval; /* set kill character */
- while (bp > logname) {
- (void) write(1, erase[cp->parity], 3);
- bp--;
- }
- break;
- case CTL('D'):
- exit(0);
- default:
- if (!isascii(ascval) || !isprint(ascval)) {
- /* ignore garbage characters */ ;
- } else if (bp - logname >= sizeof(logname) - 1) {
- error("%s: input overrun", op->tty);
- } else {
- (void) write(1, &c, 1); /* echo the character */
- *bp++ = ascval; /* and store it */
- }
- break;
- }
- }
- }
- cp->capslock = caps_lock(logname); /* upper case w/o lower case? */
- return (logname);
- }
-
- /* termio_final - set the final tty mode bits */
-
- termio_final(tp, cp)
- struct termio *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;
- tp->c_oflag |= OPOST;
- tp->c_cc[VEOF] = DEF_EOF;
- tp->c_cc[VEOL] = DEF_EOL;
- tp->c_cc[VINTR] = DEF_INTR;
- tp->c_cc[VQUIT] = DEF_QUIT;
- tp->c_cc[VKILL] = DEF_KILL;
- tp->c_cc[VERASE] = DEF_ERASE;
- tp->c_cc[VSWTCH] = DEF_SWITCH;
-
- /* 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) {
- tp->c_iflag |= IUCLC;
- tp->c_lflag |= XCASE;
- tp->c_oflag |= OLCUC;
- }
- /* Finally, make the new settings effective */
-
- (void) ioctl(0, TCSETA, tp);
- }
-
- /* caps_lock - string contains upper case without lower case */
-
- caps_lock(s)
- char *s;
- {
- int hascaps;
-
- for (hascaps = 0; *s; s++) {
- if (islower(*s))
- return (0);
- if (hascaps == 0)
- hascaps = isupper(*s);
- }
- return (hascaps);
- }
-
- /* bcode - convert speed string to speed code; return 0 on failure */
-
- bcode(s)
- char *s;
- {
- struct Speedtab {
- int 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,
- 19200, EXTA,
- 0, 0,
- };
- struct Speedtab *sp;
- int speed = atoi(s);
-
- for (sp = speedtab; sp->speed; sp++)
- if (sp->speed == speed)
- return (sp->code);
- return (0);
- }
-
- /* usage - explain */
-
- usage()
- {
- static char args[] =
- "[-a alternate_rates] [-h] [-i] [-m] [-t timeout] line baud_rate";
-
- error("usage: %s %s", progname, args);
- }
-
- /* error - report errors to the console; only understands %s */
-
- #define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
-
- /* VARARGS */
-
- error(va_alist)
- va_dcl
- {
- va_list ap;
- char *fmt;
- int fd;
- int err = errno;
- char buf[BUFSIZ];
- char *bp;
-
- if ((fd = open("/dev/console", 1)) >= 0) {
- (void) str2cpy(buf, progname, ": ");
- bp = buf + strlen(buf);
-
- /*
- * %s expansion is done by hand. The program would become three times
- * as big if we would use the stdio library...
- */
-
- va_start(ap);
- fmt = va_arg(ap, char *);
- while (*fmt) {
- if (strncmp(fmt, "%s", 2) == 0) {
- (void) strcat(bp, va_arg(ap, char *));
- bp += strlen(bp);
- fmt += 2;
- } else {
- *bp++ = *fmt++;
- }
- }
- *bp = 0;
- va_end(ap);
-
- /* Add system error message if errno was set */
-
- if (err)
- (void) str2cpy(bp, ": ", sys_errlist[errno]);
-
- /* Terminate with CR-LF since the console mode is unknown */
-
- (void) strcat(bp, "\r\n");
- (void) write(fd, buf, strlen(buf));
- (void) close(fd);
- }
- (void) sleep(5); /* be kind to init */
- exit(1);
- }
-