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