Oops, fix up my previous commit properly.
[dragonfly.git] / libexec / getty / main.c
blob4e299e60a586e734f9c9e972b74fdfade533b965
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. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
29 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved.
30 * @(#)from: main.c 8.1 (Berkeley) 6/20/93
31 * $FreeBSD: src/libexec/getty/main.c,v 1.28.2.4 2003/02/06 11:45:31 sobomax Exp $
34 #include <sys/param.h>
35 #include <sys/stat.h>
36 #include <sys/ioctl.h>
37 #include <sys/resource.h>
38 #include <sys/ttydefaults.h>
39 #include <sys/utsname.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <locale.h>
44 #include <libutil.h>
45 #include <signal.h>
46 #include <setjmp.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <syslog.h>
50 #include <termios.h>
51 #include <time.h>
52 #include <unistd.h>
54 #include "gettytab.h"
55 #include "pathnames.h"
56 #include "extern.h"
59 * Set the amount of running time that getty should accumulate
60 * before deciding that something is wrong and exit.
62 #define GETTY_TIMEOUT 60 /* seconds */
64 #undef CTRL
65 #define CTRL(x) (x&037)
67 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
69 #define PPP_FRAME 0x7e /* PPP Framing character */
70 #define PPP_STATION 0xff /* "All Station" character */
71 #define PPP_ESCAPE 0x7d /* Escape Character */
72 #define PPP_CONTROL 0x03 /* PPP Control Field */
73 #define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */
74 #define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */
75 #define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */
77 struct termios tmode, omode;
79 int crmod, digit, lower, upper;
81 char hostname[MAXHOSTNAMELEN];
82 char name[MAXLOGNAME*3];
83 char dev[] = _PATH_DEV;
84 char ttyn[32];
86 #define OBUFSIZ 128
87 #define TABBUFSIZ 512
89 char defent[TABBUFSIZ];
90 char tabent[TABBUFSIZ];
92 char *env[128];
94 char partab[] = {
95 0001,0201,0201,0001,0201,0001,0001,0201,
96 0202,0004,0003,0205,0005,0206,0201,0001,
97 0201,0001,0001,0201,0001,0201,0201,0001,
98 0001,0201,0201,0001,0201,0001,0001,0201,
99 0200,0000,0000,0200,0000,0200,0200,0000,
100 0000,0200,0200,0000,0200,0000,0000,0200,
101 0000,0200,0200,0000,0200,0000,0000,0200,
102 0200,0000,0000,0200,0000,0200,0200,0000,
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 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,0201
113 #define ERASE tmode.c_cc[VERASE]
114 #define KILL tmode.c_cc[VKILL]
115 #define EOT tmode.c_cc[VEOF]
117 #define puts Gputs
119 static void dingdong(int);
120 static int getname(void);
121 static void interrupt(int);
122 static void oflush(void);
123 static void prompt(void);
124 static void putchr(int);
125 static void putf(const char *);
126 static void putpad(const char *);
127 static void puts(const char *);
128 static void timeoverrun(int);
129 static char *get_line(int);
130 static void setttymode(const char *, int);
131 static void setdefttymode(const char *);
132 static int opentty(const char *, int);
134 jmp_buf timeout;
136 static void
137 dingdong(int signo)
139 alarm(0);
140 longjmp(timeout, 1);
143 jmp_buf intrupt;
145 static void
146 interrupt(int signo)
148 longjmp(intrupt, 1);
152 * Action to take when getty is running too long.
154 static void
155 timeoverrun(int signo)
157 syslog(LOG_ERR, "getty exiting due to excessive running time");
158 exit(1);
162 main(int argc, char **argv)
164 extern char **environ;
165 const char *tname;
166 int first_sleep = 1, first_time = 1;
167 struct rlimit limit;
168 int rval;
170 signal(SIGINT, SIG_IGN);
171 signal(SIGQUIT, SIG_IGN);
173 openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
174 gethostname(hostname, sizeof(hostname) - 1);
175 hostname[sizeof(hostname) - 1] = '\0';
176 if (hostname[0] == '\0')
177 strcpy(hostname, "Amnesiac");
180 * Limit running time to deal with broken or dead lines.
182 (void)signal(SIGXCPU, timeoverrun);
183 limit.rlim_max = RLIM_INFINITY;
184 limit.rlim_cur = GETTY_TIMEOUT;
185 (void)setrlimit(RLIMIT_CPU, &limit);
187 gettable("default", defent);
188 gendefaults();
189 tname = "default";
190 if (argc > 1)
191 tname = argv[1];
194 * The following is a work around for vhangup interactions
195 * which cause great problems getting window systems started.
196 * If the tty line is "-", we do the old style getty presuming
197 * that the file descriptors are already set up for us.
198 * J. Gettys - MIT Project Athena.
200 if (argc <= 2 || strcmp(argv[2], "-") == 0)
201 strcpy(ttyn, ttyname(STDIN_FILENO));
202 else {
203 strcpy(ttyn, dev);
204 strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
205 if (strcmp(argv[0], "+") != 0) {
206 chown(ttyn, 0, 0);
207 chmod(ttyn, 0600);
208 revoke(ttyn);
210 gettable(tname, tabent);
212 /* Init modem sequence has been specified
214 if (IC) {
215 if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
216 exit(1);
217 setdefttymode(tname);
218 if (getty_chat(IC, CT, DC) > 0) {
219 syslog(LOG_ERR, "modem init problem on %s", ttyn);
220 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
221 exit(1);
225 if (AC) {
226 int i, rfds;
227 struct timeval timeout;
229 if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
230 exit(1);
231 setdefttymode(tname);
232 rfds = 1 << 0; /* FD_SET */
233 timeout.tv_sec = RT;
234 timeout.tv_usec = 0;
235 i = select(32, (fd_set*)&rfds, NULL,
236 NULL, RT ? &timeout : NULL);
237 if (i < 0) {
238 syslog(LOG_ERR, "select %s: %m", ttyn);
239 } else if (i == 0) {
240 syslog(LOG_NOTICE, "recycle tty %s", ttyn);
241 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
242 exit(0); /* recycle for init */
244 i = getty_chat(AC, CT, DC);
245 if (i > 0) {
246 syslog(LOG_ERR, "modem answer problem on %s", ttyn);
247 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
248 exit(1);
250 } else { /* maybe blocking open */
251 if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
252 exit(1);
257 /* Start with default tty settings */
258 if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
259 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
260 exit(1);
263 * Don't rely on the driver too much, and initialize crucial
264 * things according to <sys/ttydefaults.h>. Avoid clobbering
265 * the c_cc[] settings however, the console drivers might wish
266 * to leave their idea of the preferred VERASE key value
267 * there.
269 tmode.c_iflag = TTYDEF_IFLAG;
270 tmode.c_oflag = TTYDEF_OFLAG;
271 tmode.c_lflag = TTYDEF_LFLAG;
272 tmode.c_cflag = TTYDEF_CFLAG;
273 tmode.c_cflag |= (NC ? CLOCAL : 0);
274 omode = tmode;
276 for (;;) {
279 * if a delay was specified then sleep for that
280 * number of seconds before writing the initial prompt
282 if (first_sleep && DE) {
283 sleep(DE);
284 /* remove any noise */
285 (void)tcflush(STDIN_FILENO, TCIOFLUSH);
287 first_sleep = 0;
289 setttymode(tname, 0);
290 if (AB) {
291 tname = autobaud();
292 continue;
294 if (PS) {
295 tname = portselector();
296 continue;
298 if (CL && *CL)
299 putpad(CL);
300 edithost(HE);
302 /* if this is the first time through this, and an
303 issue file has been given, then send it */
304 if (first_time && IF) {
305 int fd;
307 if ((fd = open(IF, O_RDONLY)) != -1) {
308 char * cp;
310 while ((cp = get_line(fd)) != NULL) {
311 putf(cp);
313 close(fd);
316 first_time = 0;
318 if (IM && *IM && !(PL && PP))
319 putf(IM);
320 if (setjmp(timeout)) {
321 cfsetispeed(&tmode, B0);
322 cfsetospeed(&tmode, B0);
323 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
324 exit(1);
326 if (TO) {
327 signal(SIGALRM, dingdong);
328 alarm(TO);
330 if (AL) {
331 const char *p = AL;
332 char *q = name;
334 while (*p && q < &name[sizeof name - 1]) {
335 if (isupper(*p))
336 upper = 1;
337 else if (islower(*p))
338 lower = 1;
339 else if (isdigit(*p))
340 digit++;
341 *q++ = *p++;
343 } else if (!(PL && PP))
344 rval = getname();
345 if (rval == 2 || (PL && PP)) {
346 oflush();
347 alarm(0);
348 limit.rlim_max = RLIM_INFINITY;
349 limit.rlim_cur = RLIM_INFINITY;
350 (void)setrlimit(RLIMIT_CPU, &limit);
351 execle(PP, "ppplogin", ttyn, NULL, env);
352 syslog(LOG_ERR, "%s: %m", PP);
353 exit(1);
354 } else if (rval || AL) {
355 int i;
357 oflush();
358 alarm(0);
359 signal(SIGALRM, SIG_DFL);
360 if (name[0] == '-') {
361 puts("user names may not start with '-'.");
362 continue;
364 if (!(upper || lower || digit))
365 continue;
366 set_flags(2);
367 if (crmod) {
368 tmode.c_iflag |= ICRNL;
369 tmode.c_oflag |= ONLCR;
371 #if REALLY_OLD_TTYS
372 if (upper || UC)
373 tmode.sg_flags |= LCASE;
374 if (lower || LC)
375 tmode.sg_flags &= ~LCASE;
376 #endif
377 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
378 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
379 exit(1);
381 signal(SIGINT, SIG_DFL);
382 for (i = 0; environ[i] != NULL; i++)
383 env[i] = environ[i];
384 makeenv(&env[i]);
386 limit.rlim_max = RLIM_INFINITY;
387 limit.rlim_cur = RLIM_INFINITY;
388 (void)setrlimit(RLIMIT_CPU, &limit);
389 execle(LO, "login", AL ? "-fp" : "-p", name,
390 NULL, env);
391 syslog(LOG_ERR, "%s: %m", LO);
392 exit(1);
394 alarm(0);
395 signal(SIGALRM, SIG_DFL);
396 signal(SIGINT, SIG_IGN);
397 if (NX && *NX)
398 tname = NX;
402 static int
403 opentty(const char *ttyn, int flags)
405 int i, j = 0;
406 int failopenlogged = 0;
408 while (j < 10 && (i = open(ttyn, flags)) == -1)
410 if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) {
411 syslog(LOG_ERR, "open %s: %m", ttyn);
412 failopenlogged = 1;
414 j++;
415 sleep(60);
417 if (i == -1) {
418 syslog(LOG_ERR, "open %s: %m", ttyn);
419 return 0;
421 else {
422 if (login_tty(i) < 0) {
423 if (daemon(0,0) < 0) {
424 syslog(LOG_ERR,"daemon: %m");
425 close(i);
426 return 0;
428 if (login_tty(i) < 0) {
429 syslog(LOG_ERR, "login_tty %s: %m", ttyn);
430 close(i);
431 return 0;
434 return 1;
438 static void
439 setdefttymode(const char *tname)
441 if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
442 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
443 exit(1);
445 tmode.c_iflag = TTYDEF_IFLAG;
446 tmode.c_oflag = TTYDEF_OFLAG;
447 tmode.c_lflag = TTYDEF_LFLAG;
448 tmode.c_cflag = TTYDEF_CFLAG;
449 omode = tmode;
450 setttymode(tname, 1);
453 static void
454 setttymode(const char *tname, int raw)
456 int off = 0;
458 gettable(tname, tabent);
459 if (OPset || EPset || APset)
460 APset++, OPset++, EPset++;
461 setdefaults();
462 (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
463 ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */
464 ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */
466 if (IS)
467 cfsetispeed(&tmode, speed(IS));
468 else if (SP)
469 cfsetispeed(&tmode, speed(SP));
470 if (OS)
471 cfsetospeed(&tmode, speed(OS));
472 else if (SP)
473 cfsetospeed(&tmode, speed(SP));
474 set_flags(0);
475 setchars();
476 if (raw)
477 cfmakeraw(&tmode);
478 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
479 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
480 exit(1);
485 static int
486 getname(void)
488 int c;
489 char *np;
490 unsigned char cs;
491 int ppp_state = 0;
492 int ppp_connection = 0;
495 * Interrupt may happen if we use CBREAK mode
497 if (setjmp(intrupt)) {
498 signal(SIGINT, SIG_IGN);
499 return (0);
501 signal(SIGINT, interrupt);
502 set_flags(1);
503 prompt();
504 oflush();
505 if (PF > 0) {
506 sleep(PF);
507 PF = 0;
509 if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
510 syslog(LOG_ERR, "%s: %m", ttyn);
511 exit(1);
513 crmod = digit = lower = upper = 0;
514 np = name;
515 for (;;) {
516 oflush();
517 if (read(STDIN_FILENO, &cs, 1) <= 0)
518 exit(0);
519 if ((c = cs&0177) == 0)
520 return (0);
522 /* PPP detection state machine..
523 Look for sequences:
524 PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
525 PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
526 See RFC1662.
527 Derived from code from Michael Hancock, <michaelh@cet.co.jp>
528 and Erik 'PPP' Olson, <eriko@wrq.com>
531 if (PP && (cs == PPP_FRAME)) {
532 ppp_state = 1;
533 } else if (ppp_state == 1 && cs == PPP_STATION) {
534 ppp_state = 2;
535 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
536 ppp_state = 3;
537 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
538 || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
539 ppp_state = 4;
540 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
541 ppp_state = 5;
542 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
543 ppp_connection = 1;
544 break;
545 } else {
546 ppp_state = 0;
549 if (c == EOT || c == CTRL('d'))
550 exit(1);
551 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
552 putf("\r\n");
553 break;
555 if (islower(c))
556 lower = 1;
557 else if (isupper(c))
558 upper = 1;
559 else if (c == ERASE || c == '\b' || c == 0177) {
560 if (np > name) {
561 np--;
562 if (cfgetospeed(&tmode) >= 1200)
563 puts("\b \b");
564 else
565 putchr(cs);
567 continue;
568 } else if (c == KILL || c == CTRL('u')) {
569 putchr('\r');
570 if (cfgetospeed(&tmode) < 1200)
571 putchr('\n');
572 /* this is the way they do it down under ... */
573 else if (np > name)
574 puts(" \r");
575 prompt();
576 np = name;
577 continue;
578 } else if (isdigit(c))
579 digit++;
580 if (IG && (c <= ' ' || c > 0176))
581 continue;
582 *np++ = c;
583 putchr(cs);
585 signal(SIGINT, SIG_IGN);
586 *np = 0;
587 if (c == '\r')
588 crmod = 1;
589 if ((upper && !lower && !LC) || UC)
590 for (np = name; *np; np++)
591 if (isupper(*np))
592 *np = tolower(*np);
593 return (1 + ppp_connection);
596 static void
597 putpad(const char *s)
599 int pad = 0;
600 speed_t ospeed;
602 ospeed = cfgetospeed(&tmode);
604 if (isdigit(*s)) {
605 while (isdigit(*s)) {
606 pad *= 10;
607 pad += *s++ - '0';
609 pad *= 10;
610 if (*s == '.' && isdigit(s[1])) {
611 pad += s[1] - '0';
612 s += 2;
616 puts(s);
618 * If no delay needed, or output speed is
619 * not comprehensible, then don't try to delay.
621 if (pad == 0 || ospeed <= 0)
622 return;
625 * Round up by a half a character frame, and then do the delay.
626 * Too bad there are no user program accessible programmed delays.
627 * Transmitting pad characters slows many terminals down and also
628 * loads the system.
630 pad = (pad * ospeed + 50000) / 100000;
631 while (pad--)
632 putchr(*PC);
635 static void
636 puts(const char *s)
638 while (*s)
639 putchr(*s++);
642 char outbuf[OBUFSIZ];
643 int obufcnt = 0;
645 static void
646 putchr(int cc)
648 char c;
650 c = cc;
651 if (!NP) {
652 c |= partab[c&0177] & 0200;
653 if (OP)
654 c ^= 0200;
656 if (!UB) {
657 outbuf[obufcnt++] = c;
658 if (obufcnt >= OBUFSIZ)
659 oflush();
660 } else
661 write(STDOUT_FILENO, &c, 1);
664 static void
665 oflush(void)
667 if (obufcnt)
668 write(STDOUT_FILENO, outbuf, obufcnt);
669 obufcnt = 0;
672 static void
673 prompt(void)
675 putf(LM);
676 if (CO)
677 putchr('\n');
681 static char *
682 get_line(int fd)
684 int i = 0;
685 static char linebuf[512];
688 * This is certainly slow, but it avoids having to include
689 * stdio.h unnecessarily. Issue files should be small anyway.
691 while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
692 if (linebuf[i] == '\n') {
693 /* Don't rely on newline mode, assume raw */
694 linebuf[i++] = '\r';
695 linebuf[i++] = '\n';
696 linebuf[i] = '\0';
697 return linebuf;
699 ++i;
701 linebuf[i] = '\0';
702 return i ? linebuf : 0;
705 static void
706 putf(const char *cp)
708 extern char editedhost[];
709 time_t t;
710 char *slash, db[100];
712 static struct utsname kerninfo;
714 if (!*kerninfo.sysname)
715 uname(&kerninfo);
717 while (*cp) {
718 if (*cp != '%') {
719 putchr(*cp++);
720 continue;
722 switch (*++cp) {
724 case 't':
725 slash = strrchr(ttyn, '/');
726 if (slash == NULL)
727 puts(ttyn);
728 else
729 puts(&slash[1]);
730 break;
732 case 'h':
733 puts(editedhost);
734 break;
736 case 'd': {
737 t = (time_t)0;
738 (void)time(&t);
739 if (Lo)
740 (void)setlocale(LC_TIME, Lo);
741 (void)strftime(db, sizeof(db), DF, localtime(&t));
742 puts(db);
743 break;
745 case 's':
746 puts(kerninfo.sysname);
747 break;
749 case 'm':
750 puts(kerninfo.machine);
751 break;
753 case 'r':
754 puts(kerninfo.release);
755 break;
757 case 'v':
758 puts(kerninfo.version);
759 break;
762 case '%':
763 putchr('%');
764 break;
766 cp++;