boot - Use ether_sprintf() where appropriate.
[dragonfly.git] / libexec / getty / main.c
blob2b96431fd2d905c2d2fe0f1479113e21fca2caf4
1 /*-
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
33 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved.
34 * @(#)from: main.c 8.1 (Berkeley) 6/20/93
35 * $FreeBSD: src/libexec/getty/main.c,v 1.28.2.4 2003/02/06 11:45:31 sobomax Exp $
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <sys/ioctl.h>
41 #include <sys/resource.h>
42 #include <sys/ttydefaults.h>
43 #include <sys/utsname.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <locale.h>
48 #include <libutil.h>
49 #include <signal.h>
50 #include <setjmp.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <syslog.h>
54 #include <termios.h>
55 #include <time.h>
56 #include <unistd.h>
58 #include "gettytab.h"
59 #include "pathnames.h"
60 #include "extern.h"
63 * Set the amount of running time that getty should accumulate
64 * before deciding that something is wrong and exit.
66 #define GETTY_TIMEOUT 60 /* seconds */
68 #undef CTRL
69 #define CTRL(x) (x&037)
71 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
73 #define PPP_FRAME 0x7e /* PPP Framing character */
74 #define PPP_STATION 0xff /* "All Station" character */
75 #define PPP_ESCAPE 0x7d /* Escape Character */
76 #define PPP_CONTROL 0x03 /* PPP Control Field */
77 #define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */
78 #define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */
79 #define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */
81 struct termios tmode, omode;
83 int crmod, digit, lower, upper;
85 char hostname[MAXHOSTNAMELEN];
86 char name[MAXLOGNAME*3];
87 char dev[] = _PATH_DEV;
88 char ttyn[32];
90 #define OBUFSIZ 128
91 #define TABBUFSIZ 512
93 char defent[TABBUFSIZ];
94 char tabent[TABBUFSIZ];
96 char *env[128];
98 char partab[] = {
99 0001,0201,0201,0001,0201,0001,0001,0201,
100 0202,0004,0003,0205,0005,0206,0201,0001,
101 0201,0001,0001,0201,0001,0201,0201,0001,
102 0001,0201,0201,0001,0201,0001,0001,0201,
103 0200,0000,0000,0200,0000,0200,0200,0000,
104 0000,0200,0200,0000,0200,0000,0000,0200,
105 0000,0200,0200,0000,0200,0000,0000,0200,
106 0200,0000,0000,0200,0000,0200,0200,0000,
107 0200,0000,0000,0200,0000,0200,0200,0000,
108 0000,0200,0200,0000,0200,0000,0000,0200,
109 0000,0200,0200,0000,0200,0000,0000,0200,
110 0200,0000,0000,0200,0000,0200,0200,0000,
111 0000,0200,0200,0000,0200,0000,0000,0200,
112 0200,0000,0000,0200,0000,0200,0200,0000,
113 0200,0000,0000,0200,0000,0200,0200,0000,
114 0000,0200,0200,0000,0200,0000,0000,0201
117 #define ERASE tmode.c_cc[VERASE]
118 #define KILL tmode.c_cc[VKILL]
119 #define EOT tmode.c_cc[VEOF]
121 #define puts Gputs
123 static void dingdong (int);
124 static int getname (void);
125 static void interrupt (int);
126 static void oflush (void);
127 static void prompt (void);
128 static void putchr (int);
129 static void putf (const char *);
130 static void putpad (const char *);
131 static void puts (const char *);
132 static void timeoverrun (int);
133 static char *getline (int);
134 static void setttymode (const char *, int);
135 static void setdefttymode (const char *);
136 static int opentty (const char *, int);
138 int main (int, char **);
140 jmp_buf timeout;
142 static void
143 dingdong(int signo)
145 alarm(0);
146 longjmp(timeout, 1);
149 jmp_buf intrupt;
151 static void
152 interrupt(int signo)
154 longjmp(intrupt, 1);
158 * Action to take when getty is running too long.
160 static void
161 timeoverrun(int signo)
163 syslog(LOG_ERR, "getty exiting due to excessive running time");
164 exit(1);
168 main(int argc, char **argv)
170 extern char **environ;
171 const char *tname;
172 int first_sleep = 1, first_time = 1;
173 struct rlimit limit;
174 int rval;
176 signal(SIGINT, SIG_IGN);
177 signal(SIGQUIT, SIG_IGN);
179 openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
180 gethostname(hostname, sizeof(hostname) - 1);
181 hostname[sizeof(hostname) - 1] = '\0';
182 if (hostname[0] == '\0')
183 strcpy(hostname, "Amnesiac");
186 * Limit running time to deal with broken or dead lines.
188 (void)signal(SIGXCPU, timeoverrun);
189 limit.rlim_max = RLIM_INFINITY;
190 limit.rlim_cur = GETTY_TIMEOUT;
191 (void)setrlimit(RLIMIT_CPU, &limit);
193 gettable("default", defent);
194 gendefaults();
195 tname = "default";
196 if (argc > 1)
197 tname = argv[1];
200 * The following is a work around for vhangup interactions
201 * which cause great problems getting window systems started.
202 * If the tty line is "-", we do the old style getty presuming
203 * that the file descriptors are already set up for us.
204 * J. Gettys - MIT Project Athena.
206 if (argc <= 2 || strcmp(argv[2], "-") == 0)
207 strcpy(ttyn, ttyname(STDIN_FILENO));
208 else {
209 strcpy(ttyn, dev);
210 strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
211 if (strcmp(argv[0], "+") != 0) {
212 chown(ttyn, 0, 0);
213 chmod(ttyn, 0600);
214 revoke(ttyn);
216 gettable(tname, tabent);
218 /* Init modem sequence has been specified
220 if (IC) {
221 if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
222 exit(1);
223 setdefttymode(tname);
224 if (getty_chat(IC, CT, DC) > 0) {
225 syslog(LOG_ERR, "modem init problem on %s", ttyn);
226 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
227 exit(1);
231 if (AC) {
232 int i, rfds;
233 struct timeval timeout;
235 if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
236 exit(1);
237 setdefttymode(tname);
238 rfds = 1 << 0; /* FD_SET */
239 timeout.tv_sec = RT;
240 timeout.tv_usec = 0;
241 i = select(32, (fd_set*)&rfds, NULL,
242 NULL, RT ? &timeout : NULL);
243 if (i < 0) {
244 syslog(LOG_ERR, "select %s: %m", ttyn);
245 } else if (i == 0) {
246 syslog(LOG_NOTICE, "recycle tty %s", ttyn);
247 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
248 exit(0); /* recycle for init */
250 i = getty_chat(AC, CT, DC);
251 if (i > 0) {
252 syslog(LOG_ERR, "modem answer problem on %s", ttyn);
253 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
254 exit(1);
256 } else { /* maybe blocking open */
257 if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
258 exit(1);
263 /* Start with default tty settings */
264 if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
265 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
266 exit(1);
269 * Don't rely on the driver too much, and initialize crucial
270 * things according to <sys/ttydefaults.h>. Avoid clobbering
271 * the c_cc[] settings however, the console drivers might wish
272 * to leave their idea of the preferred VERASE key value
273 * there.
275 tmode.c_iflag = TTYDEF_IFLAG;
276 tmode.c_oflag = TTYDEF_OFLAG;
277 tmode.c_lflag = TTYDEF_LFLAG;
278 tmode.c_cflag = TTYDEF_CFLAG;
279 tmode.c_cflag |= (NC ? CLOCAL : 0);
280 omode = tmode;
282 for (;;) {
285 * if a delay was specified then sleep for that
286 * number of seconds before writing the initial prompt
288 if (first_sleep && DE) {
289 sleep(DE);
290 /* remove any noise */
291 (void)tcflush(STDIN_FILENO, TCIOFLUSH);
293 first_sleep = 0;
295 setttymode(tname, 0);
296 if (AB) {
297 tname = autobaud();
298 continue;
300 if (PS) {
301 tname = portselector();
302 continue;
304 if (CL && *CL)
305 putpad(CL);
306 edithost(HE);
308 /* if this is the first time through this, and an
309 issue file has been given, then send it */
310 if (first_time && IF) {
311 int fd;
313 if ((fd = open(IF, O_RDONLY)) != -1) {
314 char * cp;
316 while ((cp = getline(fd)) != NULL) {
317 putf(cp);
319 close(fd);
322 first_time = 0;
324 if (IM && *IM && !(PL && PP))
325 putf(IM);
326 if (setjmp(timeout)) {
327 cfsetispeed(&tmode, B0);
328 cfsetospeed(&tmode, B0);
329 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
330 exit(1);
332 if (TO) {
333 signal(SIGALRM, dingdong);
334 alarm(TO);
336 if (AL) {
337 const char *p = AL;
338 char *q = name;
340 while (*p && q < &name[sizeof name - 1]) {
341 if (isupper(*p))
342 upper = 1;
343 else if (islower(*p))
344 lower = 1;
345 else if (isdigit(*p))
346 digit++;
347 *q++ = *p++;
349 } else if (!(PL && PP))
350 rval = getname();
351 if (rval == 2 || (PL && PP)) {
352 oflush();
353 alarm(0);
354 limit.rlim_max = RLIM_INFINITY;
355 limit.rlim_cur = RLIM_INFINITY;
356 (void)setrlimit(RLIMIT_CPU, &limit);
357 execle(PP, "ppplogin", ttyn, NULL, env);
358 syslog(LOG_ERR, "%s: %m", PP);
359 exit(1);
360 } else if (rval || AL) {
361 int i;
363 oflush();
364 alarm(0);
365 signal(SIGALRM, SIG_DFL);
366 if (name[0] == '-') {
367 puts("user names may not start with '-'.");
368 continue;
370 if (!(upper || lower || digit))
371 continue;
372 set_flags(2);
373 if (crmod) {
374 tmode.c_iflag |= ICRNL;
375 tmode.c_oflag |= ONLCR;
377 #if REALLY_OLD_TTYS
378 if (upper || UC)
379 tmode.sg_flags |= LCASE;
380 if (lower || LC)
381 tmode.sg_flags &= ~LCASE;
382 #endif
383 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
384 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
385 exit(1);
387 signal(SIGINT, SIG_DFL);
388 for (i = 0; environ[i] != NULL; i++)
389 env[i] = environ[i];
390 makeenv(&env[i]);
392 limit.rlim_max = RLIM_INFINITY;
393 limit.rlim_cur = RLIM_INFINITY;
394 (void)setrlimit(RLIMIT_CPU, &limit);
395 execle(LO, "login", AL ? "-fp" : "-p", name,
396 NULL, env);
397 syslog(LOG_ERR, "%s: %m", LO);
398 exit(1);
400 alarm(0);
401 signal(SIGALRM, SIG_DFL);
402 signal(SIGINT, SIG_IGN);
403 if (NX && *NX)
404 tname = NX;
408 static int
409 opentty(const char *ttyn, int flags)
411 int i, j = 0;
412 int failopenlogged = 0;
414 while (j < 10 && (i = open(ttyn, flags)) == -1)
416 if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
417 syslog(LOG_ERR, "open %s: %m", ttyn);
418 failopenlogged = 1;
420 j++;
421 sleep(60);
423 if (i == -1) {
424 syslog(LOG_ERR, "open %s: %m", ttyn);
425 return 0;
427 else {
428 if (login_tty(i) < 0) {
429 if (daemon(0,0) < 0) {
430 syslog(LOG_ERR,"daemon: %m");
431 close(i);
432 return 0;
434 if (login_tty(i) < 0) {
435 syslog(LOG_ERR, "login_tty %s: %m", ttyn);
436 close(i);
437 return 0;
440 return 1;
444 static void
445 setdefttymode(const char *tname)
447 if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
448 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
449 exit(1);
451 tmode.c_iflag = TTYDEF_IFLAG;
452 tmode.c_oflag = TTYDEF_OFLAG;
453 tmode.c_lflag = TTYDEF_LFLAG;
454 tmode.c_cflag = TTYDEF_CFLAG;
455 omode = tmode;
456 setttymode(tname, 1);
459 static void
460 setttymode(const char *tname, int raw)
462 int off = 0;
464 gettable(tname, tabent);
465 if (OPset || EPset || APset)
466 APset++, OPset++, EPset++;
467 setdefaults();
468 (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
469 ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */
470 ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */
472 if (IS)
473 cfsetispeed(&tmode, speed(IS));
474 else if (SP)
475 cfsetispeed(&tmode, speed(SP));
476 if (OS)
477 cfsetospeed(&tmode, speed(OS));
478 else if (SP)
479 cfsetospeed(&tmode, speed(SP));
480 set_flags(0);
481 setchars();
482 if (raw)
483 cfmakeraw(&tmode);
484 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
485 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
486 exit(1);
491 static int
492 getname(void)
494 int c;
495 char *np;
496 unsigned char cs;
497 int ppp_state = 0;
498 int ppp_connection = 0;
501 * Interrupt may happen if we use CBREAK mode
503 if (setjmp(intrupt)) {
504 signal(SIGINT, SIG_IGN);
505 return (0);
507 signal(SIGINT, interrupt);
508 set_flags(1);
509 prompt();
510 oflush();
511 if (PF > 0) {
512 sleep(PF);
513 PF = 0;
515 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
516 syslog(LOG_ERR, "%s: %m", ttyn);
517 exit(1);
519 crmod = digit = lower = upper = 0;
520 np = name;
521 for (;;) {
522 oflush();
523 if (read(STDIN_FILENO, &cs, 1) <= 0)
524 exit(0);
525 if ((c = cs&0177) == 0)
526 return (0);
528 /* PPP detection state machine..
529 Look for sequences:
530 PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
531 PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
532 See RFC1662.
533 Derived from code from Michael Hancock, <michaelh@cet.co.jp>
534 and Erik 'PPP' Olson, <eriko@wrq.com>
537 if (PP && (cs == PPP_FRAME)) {
538 ppp_state = 1;
539 } else if (ppp_state == 1 && cs == PPP_STATION) {
540 ppp_state = 2;
541 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
542 ppp_state = 3;
543 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
544 || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
545 ppp_state = 4;
546 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
547 ppp_state = 5;
548 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
549 ppp_connection = 1;
550 break;
551 } else {
552 ppp_state = 0;
555 if (c == EOT || c == CTRL('d'))
556 exit(1);
557 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
558 putf("\r\n");
559 break;
561 if (islower(c))
562 lower = 1;
563 else if (isupper(c))
564 upper = 1;
565 else if (c == ERASE || c == '\b' || c == 0177) {
566 if (np > name) {
567 np--;
568 if (cfgetospeed(&tmode) >= 1200)
569 puts("\b \b");
570 else
571 putchr(cs);
573 continue;
574 } else if (c == KILL || c == CTRL('u')) {
575 putchr('\r');
576 if (cfgetospeed(&tmode) < 1200)
577 putchr('\n');
578 /* this is the way they do it down under ... */
579 else if (np > name)
580 puts(" \r");
581 prompt();
582 np = name;
583 continue;
584 } else if (isdigit(c))
585 digit++;
586 if (IG && (c <= ' ' || c > 0176))
587 continue;
588 *np++ = c;
589 putchr(cs);
591 signal(SIGINT, SIG_IGN);
592 *np = 0;
593 if (c == '\r')
594 crmod = 1;
595 if ((upper && !lower && !LC) || UC)
596 for (np = name; *np; np++)
597 if (isupper(*np))
598 *np = tolower(*np);
599 return (1 + ppp_connection);
602 static void
603 putpad(const char *s)
605 int pad = 0;
606 speed_t ospeed;
608 ospeed = cfgetospeed(&tmode);
610 if (isdigit(*s)) {
611 while (isdigit(*s)) {
612 pad *= 10;
613 pad += *s++ - '0';
615 pad *= 10;
616 if (*s == '.' && isdigit(s[1])) {
617 pad += s[1] - '0';
618 s += 2;
622 puts(s);
624 * If no delay needed, or output speed is
625 * not comprehensible, then don't try to delay.
627 if (pad == 0 || ospeed <= 0)
628 return;
631 * Round up by a half a character frame, and then do the delay.
632 * Too bad there are no user program accessible programmed delays.
633 * Transmitting pad characters slows many terminals down and also
634 * loads the system.
636 pad = (pad * ospeed + 50000) / 100000;
637 while (pad--)
638 putchr(*PC);
641 static void
642 puts(const char *s)
644 while (*s)
645 putchr(*s++);
648 char outbuf[OBUFSIZ];
649 int obufcnt = 0;
651 static void
652 putchr(int cc)
654 char c;
656 c = cc;
657 if (!NP) {
658 c |= partab[c&0177] & 0200;
659 if (OP)
660 c ^= 0200;
662 if (!UB) {
663 outbuf[obufcnt++] = c;
664 if (obufcnt >= OBUFSIZ)
665 oflush();
666 } else
667 write(STDOUT_FILENO, &c, 1);
670 static void
671 oflush(void)
673 if (obufcnt)
674 write(STDOUT_FILENO, outbuf, obufcnt);
675 obufcnt = 0;
678 static void
679 prompt(void)
681 putf(LM);
682 if (CO)
683 putchr('\n');
687 static char *
688 getline(int fd)
690 int i = 0;
691 static char linebuf[512];
694 * This is certainly slow, but it avoids having to include
695 * stdio.h unnecessarily. Issue files should be small anyway.
697 while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
698 if (linebuf[i] == '\n') {
699 /* Don't rely on newline mode, assume raw */
700 linebuf[i++] = '\r';
701 linebuf[i++] = '\n';
702 linebuf[i] = '\0';
703 return linebuf;
705 ++i;
707 linebuf[i] = '\0';
708 return i ? linebuf : 0;
711 static void
712 putf(const char *cp)
714 extern char editedhost[];
715 time_t t;
716 char *slash, db[100];
718 static struct utsname kerninfo;
720 if (!*kerninfo.sysname)
721 uname(&kerninfo);
723 while (*cp) {
724 if (*cp != '%') {
725 putchr(*cp++);
726 continue;
728 switch (*++cp) {
730 case 't':
731 slash = strrchr(ttyn, '/');
732 if (slash == NULL)
733 puts(ttyn);
734 else
735 puts(&slash[1]);
736 break;
738 case 'h':
739 puts(editedhost);
740 break;
742 case 'd': {
743 t = (time_t)0;
744 (void)time(&t);
745 if (Lo)
746 (void)setlocale(LC_TIME, Lo);
747 (void)strftime(db, sizeof(db), DF, localtime(&t));
748 puts(db);
749 break;
751 case 's':
752 puts(kerninfo.sysname);
753 break;
755 case 'm':
756 puts(kerninfo.machine);
757 break;
759 case 'r':
760 puts(kerninfo.release);
761 break;
763 case 'v':
764 puts(kerninfo.version);
765 break;
768 case '%':
769 putchr('%');
770 break;
772 cp++;