Add support for cmx(4) devices.
[dragonfly.git] / libexec / getty / main.c
blob942fb98fba21ed3a38bd2cc01121a4dbc0c16a29
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 $
36 * $DragonFly: src/libexec/getty/main.c,v 1.4 2004/03/26 00:30:12 cpressey Exp $
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/ioctl.h>
42 #include <sys/resource.h>
43 #include <sys/ttydefaults.h>
44 #include <sys/utsname.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <locale.h>
49 #include <libutil.h>
50 #include <signal.h>
51 #include <setjmp.h>
52 #include <signal.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <syslog.h>
56 #include <termios.h>
57 #include <time.h>
58 #include <unistd.h>
60 #include "gettytab.h"
61 #include "pathnames.h"
62 #include "extern.h"
65 * Set the amount of running time that getty should accumulate
66 * before deciding that something is wrong and exit.
68 #define GETTY_TIMEOUT 60 /* seconds */
70 #undef CTRL
71 #define CTRL(x) (x&037)
73 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
75 #define PPP_FRAME 0x7e /* PPP Framing character */
76 #define PPP_STATION 0xff /* "All Station" character */
77 #define PPP_ESCAPE 0x7d /* Escape Character */
78 #define PPP_CONTROL 0x03 /* PPP Control Field */
79 #define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */
80 #define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */
81 #define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */
83 struct termios tmode, omode;
85 int crmod, digit, lower, upper;
87 char hostname[MAXHOSTNAMELEN];
88 char name[MAXLOGNAME*3];
89 char dev[] = _PATH_DEV;
90 char ttyn[32];
92 #define OBUFSIZ 128
93 #define TABBUFSIZ 512
95 char defent[TABBUFSIZ];
96 char tabent[TABBUFSIZ];
98 char *env[128];
100 char partab[] = {
101 0001,0201,0201,0001,0201,0001,0001,0201,
102 0202,0004,0003,0205,0005,0206,0201,0001,
103 0201,0001,0001,0201,0001,0201,0201,0001,
104 0001,0201,0201,0001,0201,0001,0001,0201,
105 0200,0000,0000,0200,0000,0200,0200,0000,
106 0000,0200,0200,0000,0200,0000,0000,0200,
107 0000,0200,0200,0000,0200,0000,0000,0200,
108 0200,0000,0000,0200,0000,0200,0200,0000,
109 0200,0000,0000,0200,0000,0200,0200,0000,
110 0000,0200,0200,0000,0200,0000,0000,0200,
111 0000,0200,0200,0000,0200,0000,0000,0200,
112 0200,0000,0000,0200,0000,0200,0200,0000,
113 0000,0200,0200,0000,0200,0000,0000,0200,
114 0200,0000,0000,0200,0000,0200,0200,0000,
115 0200,0000,0000,0200,0000,0200,0200,0000,
116 0000,0200,0200,0000,0200,0000,0000,0201
119 #define ERASE tmode.c_cc[VERASE]
120 #define KILL tmode.c_cc[VKILL]
121 #define EOT tmode.c_cc[VEOF]
123 #define puts Gputs
125 static void dingdong (int);
126 static int getname (void);
127 static void interrupt (int);
128 static void oflush (void);
129 static void prompt (void);
130 static void putchr (int);
131 static void putf (const char *);
132 static void putpad (const char *);
133 static void puts (const char *);
134 static void timeoverrun (int);
135 static char *getline (int);
136 static void setttymode (const char *, int);
137 static void setdefttymode (const char *);
138 static int opentty (const char *, int);
140 int main (int, char **);
142 jmp_buf timeout;
144 static void
145 dingdong(int signo)
147 alarm(0);
148 longjmp(timeout, 1);
151 jmp_buf intrupt;
153 static void
154 interrupt(int signo)
156 longjmp(intrupt, 1);
160 * Action to take when getty is running too long.
162 static void
163 timeoverrun(int signo)
165 syslog(LOG_ERR, "getty exiting due to excessive running time");
166 exit(1);
170 main(int argc, char **argv)
172 extern char **environ;
173 const char *tname;
174 int first_sleep = 1, first_time = 1;
175 struct rlimit limit;
176 int rval;
178 signal(SIGINT, SIG_IGN);
179 signal(SIGQUIT, SIG_IGN);
181 openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
182 gethostname(hostname, sizeof(hostname) - 1);
183 hostname[sizeof(hostname) - 1] = '\0';
184 if (hostname[0] == '\0')
185 strcpy(hostname, "Amnesiac");
188 * Limit running time to deal with broken or dead lines.
190 (void)signal(SIGXCPU, timeoverrun);
191 limit.rlim_max = RLIM_INFINITY;
192 limit.rlim_cur = GETTY_TIMEOUT;
193 (void)setrlimit(RLIMIT_CPU, &limit);
195 gettable("default", defent);
196 gendefaults();
197 tname = "default";
198 if (argc > 1)
199 tname = argv[1];
202 * The following is a work around for vhangup interactions
203 * which cause great problems getting window systems started.
204 * If the tty line is "-", we do the old style getty presuming
205 * that the file descriptors are already set up for us.
206 * J. Gettys - MIT Project Athena.
208 if (argc <= 2 || strcmp(argv[2], "-") == 0)
209 strcpy(ttyn, ttyname(STDIN_FILENO));
210 else {
211 strcpy(ttyn, dev);
212 strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
213 if (strcmp(argv[0], "+") != 0) {
214 chown(ttyn, 0, 0);
215 chmod(ttyn, 0600);
216 revoke(ttyn);
218 gettable(tname, tabent);
220 /* Init modem sequence has been specified
222 if (IC) {
223 if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
224 exit(1);
225 setdefttymode(tname);
226 if (getty_chat(IC, CT, DC) > 0) {
227 syslog(LOG_ERR, "modem init problem on %s", ttyn);
228 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
229 exit(1);
233 if (AC) {
234 int i, rfds;
235 struct timeval timeout;
237 if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
238 exit(1);
239 setdefttymode(tname);
240 rfds = 1 << 0; /* FD_SET */
241 timeout.tv_sec = RT;
242 timeout.tv_usec = 0;
243 i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
244 (fd_set*)NULL, RT ? &timeout : NULL);
245 if (i < 0) {
246 syslog(LOG_ERR, "select %s: %m", ttyn);
247 } else if (i == 0) {
248 syslog(LOG_NOTICE, "recycle tty %s", ttyn);
249 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
250 exit(0); /* recycle for init */
252 i = getty_chat(AC, CT, DC);
253 if (i > 0) {
254 syslog(LOG_ERR, "modem answer problem on %s", ttyn);
255 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
256 exit(1);
258 } else { /* maybe blocking open */
259 if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
260 exit(1);
265 /* Start with default tty settings */
266 if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
267 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
268 exit(1);
271 * Don't rely on the driver too much, and initialize crucial
272 * things according to <sys/ttydefaults.h>. Avoid clobbering
273 * the c_cc[] settings however, the console drivers might wish
274 * to leave their idea of the preferred VERASE key value
275 * there.
277 tmode.c_iflag = TTYDEF_IFLAG;
278 tmode.c_oflag = TTYDEF_OFLAG;
279 tmode.c_lflag = TTYDEF_LFLAG;
280 tmode.c_cflag = TTYDEF_CFLAG;
281 tmode.c_cflag |= (NC ? CLOCAL : 0);
282 omode = tmode;
284 for (;;) {
287 * if a delay was specified then sleep for that
288 * number of seconds before writing the initial prompt
290 if (first_sleep && DE) {
291 sleep(DE);
292 /* remove any noise */
293 (void)tcflush(STDIN_FILENO, TCIOFLUSH);
295 first_sleep = 0;
297 setttymode(tname, 0);
298 if (AB) {
299 tname = autobaud();
300 continue;
302 if (PS) {
303 tname = portselector();
304 continue;
306 if (CL && *CL)
307 putpad(CL);
308 edithost(HE);
310 /* if this is the first time through this, and an
311 issue file has been given, then send it */
312 if (first_time && IF) {
313 int fd;
315 if ((fd = open(IF, O_RDONLY)) != -1) {
316 char * cp;
318 while ((cp = getline(fd)) != NULL) {
319 putf(cp);
321 close(fd);
324 first_time = 0;
326 if (IM && *IM && !(PL && PP))
327 putf(IM);
328 if (setjmp(timeout)) {
329 cfsetispeed(&tmode, B0);
330 cfsetospeed(&tmode, B0);
331 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
332 exit(1);
334 if (TO) {
335 signal(SIGALRM, dingdong);
336 alarm(TO);
338 if (AL) {
339 const char *p = AL;
340 char *q = name;
341 int n = sizeof name;
343 while (*p && q < &name[sizeof name - 1]) {
344 if (isupper(*p))
345 upper = 1;
346 else if (islower(*p))
347 lower = 1;
348 else if (isdigit(*p))
349 digit++;
350 *q++ = *p++;
352 } else if (!(PL && PP))
353 rval = getname();
354 if (rval == 2 || (PL && PP)) {
355 oflush();
356 alarm(0);
357 limit.rlim_max = RLIM_INFINITY;
358 limit.rlim_cur = RLIM_INFINITY;
359 (void)setrlimit(RLIMIT_CPU, &limit);
360 execle(PP, "ppplogin", ttyn, (char *) 0, env);
361 syslog(LOG_ERR, "%s: %m", PP);
362 exit(1);
363 } else if (rval || AL) {
364 int i;
366 oflush();
367 alarm(0);
368 signal(SIGALRM, SIG_DFL);
369 if (name[0] == '-') {
370 puts("user names may not start with '-'.");
371 continue;
373 if (!(upper || lower || digit))
374 continue;
375 set_flags(2);
376 if (crmod) {
377 tmode.c_iflag |= ICRNL;
378 tmode.c_oflag |= ONLCR;
380 #if REALLY_OLD_TTYS
381 if (upper || UC)
382 tmode.sg_flags |= LCASE;
383 if (lower || LC)
384 tmode.sg_flags &= ~LCASE;
385 #endif
386 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
387 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
388 exit(1);
390 signal(SIGINT, SIG_DFL);
391 for (i = 0; environ[i] != (char *)0; i++)
392 env[i] = environ[i];
393 makeenv(&env[i]);
395 limit.rlim_max = RLIM_INFINITY;
396 limit.rlim_cur = RLIM_INFINITY;
397 (void)setrlimit(RLIMIT_CPU, &limit);
398 execle(LO, "login", AL ? "-fp" : "-p", name,
399 (char *) 0, env);
400 syslog(LOG_ERR, "%s: %m", LO);
401 exit(1);
403 alarm(0);
404 signal(SIGALRM, SIG_DFL);
405 signal(SIGINT, SIG_IGN);
406 if (NX && *NX)
407 tname = NX;
411 static int
412 opentty(const char *ttyn, int flags)
414 int i, j = 0;
415 int failopenlogged = 0;
417 while (j < 10 && (i = open(ttyn, flags)) == -1)
419 if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
420 syslog(LOG_ERR, "open %s: %m", ttyn);
421 failopenlogged = 1;
423 j++;
424 sleep(60);
426 if (i == -1) {
427 syslog(LOG_ERR, "open %s: %m", ttyn);
428 return 0;
430 else {
431 if (login_tty(i) < 0) {
432 if (daemon(0,0) < 0) {
433 syslog(LOG_ERR,"daemon: %m");
434 close(i);
435 return 0;
437 if (login_tty(i) < 0) {
438 syslog(LOG_ERR, "login_tty %s: %m", ttyn);
439 close(i);
440 return 0;
443 return 1;
447 static void
448 setdefttymode(const char *tname)
450 if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
451 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
452 exit(1);
454 tmode.c_iflag = TTYDEF_IFLAG;
455 tmode.c_oflag = TTYDEF_OFLAG;
456 tmode.c_lflag = TTYDEF_LFLAG;
457 tmode.c_cflag = TTYDEF_CFLAG;
458 omode = tmode;
459 setttymode(tname, 1);
462 static void
463 setttymode(const char *tname, int raw)
465 int off = 0;
467 gettable(tname, tabent);
468 if (OPset || EPset || APset)
469 APset++, OPset++, EPset++;
470 setdefaults();
471 (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
472 ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */
473 ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */
475 if (IS)
476 cfsetispeed(&tmode, speed(IS));
477 else if (SP)
478 cfsetispeed(&tmode, speed(SP));
479 if (OS)
480 cfsetospeed(&tmode, speed(OS));
481 else if (SP)
482 cfsetospeed(&tmode, speed(SP));
483 set_flags(0);
484 setchars();
485 if (raw)
486 cfmakeraw(&tmode);
487 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
488 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
489 exit(1);
494 static int
495 getname(void)
497 int c;
498 char *np;
499 unsigned char cs;
500 int ppp_state = 0;
501 int ppp_connection = 0;
504 * Interrupt may happen if we use CBREAK mode
506 if (setjmp(intrupt)) {
507 signal(SIGINT, SIG_IGN);
508 return (0);
510 signal(SIGINT, interrupt);
511 set_flags(1);
512 prompt();
513 oflush();
514 if (PF > 0) {
515 sleep(PF);
516 PF = 0;
518 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
519 syslog(LOG_ERR, "%s: %m", ttyn);
520 exit(1);
522 crmod = digit = lower = upper = 0;
523 np = name;
524 for (;;) {
525 oflush();
526 if (read(STDIN_FILENO, &cs, 1) <= 0)
527 exit(0);
528 if ((c = cs&0177) == 0)
529 return (0);
531 /* PPP detection state machine..
532 Look for sequences:
533 PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
534 PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
535 See RFC1662.
536 Derived from code from Michael Hancock, <michaelh@cet.co.jp>
537 and Erik 'PPP' Olson, <eriko@wrq.com>
540 if (PP && (cs == PPP_FRAME)) {
541 ppp_state = 1;
542 } else if (ppp_state == 1 && cs == PPP_STATION) {
543 ppp_state = 2;
544 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
545 ppp_state = 3;
546 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
547 || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
548 ppp_state = 4;
549 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
550 ppp_state = 5;
551 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
552 ppp_connection = 1;
553 break;
554 } else {
555 ppp_state = 0;
558 if (c == EOT || c == CTRL('d'))
559 exit(1);
560 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
561 putf("\r\n");
562 break;
564 if (islower(c))
565 lower = 1;
566 else if (isupper(c))
567 upper = 1;
568 else if (c == ERASE || c == '\b' || c == 0177) {
569 if (np > name) {
570 np--;
571 if (cfgetospeed(&tmode) >= 1200)
572 puts("\b \b");
573 else
574 putchr(cs);
576 continue;
577 } else if (c == KILL || c == CTRL('u')) {
578 putchr('\r');
579 if (cfgetospeed(&tmode) < 1200)
580 putchr('\n');
581 /* this is the way they do it down under ... */
582 else if (np > name)
583 puts(" \r");
584 prompt();
585 np = name;
586 continue;
587 } else if (isdigit(c))
588 digit++;
589 if (IG && (c <= ' ' || c > 0176))
590 continue;
591 *np++ = c;
592 putchr(cs);
594 signal(SIGINT, SIG_IGN);
595 *np = 0;
596 if (c == '\r')
597 crmod = 1;
598 if ((upper && !lower && !LC) || UC)
599 for (np = name; *np; np++)
600 if (isupper(*np))
601 *np = tolower(*np);
602 return (1 + ppp_connection);
605 static void
606 putpad(const char *s)
608 int pad = 0;
609 speed_t ospeed;
611 ospeed = cfgetospeed(&tmode);
613 if (isdigit(*s)) {
614 while (isdigit(*s)) {
615 pad *= 10;
616 pad += *s++ - '0';
618 pad *= 10;
619 if (*s == '.' && isdigit(s[1])) {
620 pad += s[1] - '0';
621 s += 2;
625 puts(s);
627 * If no delay needed, or output speed is
628 * not comprehensible, then don't try to delay.
630 if (pad == 0 || ospeed <= 0)
631 return;
634 * Round up by a half a character frame, and then do the delay.
635 * Too bad there are no user program accessible programmed delays.
636 * Transmitting pad characters slows many terminals down and also
637 * loads the system.
639 pad = (pad * ospeed + 50000) / 100000;
640 while (pad--)
641 putchr(*PC);
644 static void
645 puts(const char *s)
647 while (*s)
648 putchr(*s++);
651 char outbuf[OBUFSIZ];
652 int obufcnt = 0;
654 static void
655 putchr(int cc)
657 char c;
659 c = cc;
660 if (!NP) {
661 c |= partab[c&0177] & 0200;
662 if (OP)
663 c ^= 0200;
665 if (!UB) {
666 outbuf[obufcnt++] = c;
667 if (obufcnt >= OBUFSIZ)
668 oflush();
669 } else
670 write(STDOUT_FILENO, &c, 1);
673 static void
674 oflush(void)
676 if (obufcnt)
677 write(STDOUT_FILENO, outbuf, obufcnt);
678 obufcnt = 0;
681 static void
682 prompt(void)
684 putf(LM);
685 if (CO)
686 putchr('\n');
690 static char *
691 getline(int fd)
693 int i = 0;
694 static char linebuf[512];
697 * This is certainly slow, but it avoids having to include
698 * stdio.h unnecessarily. Issue files should be small anyway.
700 while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
701 if (linebuf[i] == '\n') {
702 /* Don't rely on newline mode, assume raw */
703 linebuf[i++] = '\r';
704 linebuf[i++] = '\n';
705 linebuf[i] = '\0';
706 return linebuf;
708 ++i;
710 linebuf[i] = '\0';
711 return i ? linebuf : 0;
714 static void
715 putf(const char *cp)
717 extern char editedhost[];
718 time_t t;
719 char *slash, db[100];
721 static struct utsname kerninfo;
723 if (!*kerninfo.sysname)
724 uname(&kerninfo);
726 while (*cp) {
727 if (*cp != '%') {
728 putchr(*cp++);
729 continue;
731 switch (*++cp) {
733 case 't':
734 slash = strrchr(ttyn, '/');
735 if (slash == (char *) 0)
736 puts(ttyn);
737 else
738 puts(&slash[1]);
739 break;
741 case 'h':
742 puts(editedhost);
743 break;
745 case 'd': {
746 t = (time_t)0;
747 (void)time(&t);
748 if (Lo)
749 (void)setlocale(LC_TIME, Lo);
750 (void)strftime(db, sizeof(db), DF, localtime(&t));
751 puts(db);
752 break;
754 case 's':
755 puts(kerninfo.sysname);
756 break;
758 case 'm':
759 puts(kerninfo.machine);
760 break;
762 case 'r':
763 puts(kerninfo.release);
764 break;
766 case 'v':
767 puts(kerninfo.version);
768 break;
771 case '%':
772 putchr('%');
773 break;
775 cp++;