/* $NetBSD: main.c,v 1.58 2010/02/03 15:34:43 roy Exp $ */ /*- * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1980, 1993\ The Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "from: @(#)main.c 8.1 (Berkeley) 6/20/93"; #else __RCSID("$NetBSD: main.c,v 1.58 2010/02/03 15:34:43 roy Exp $"); #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gettytab.h" #include "pathnames.h" #include "extern.h" extern char editedhost[]; /* * Set the amount of running time that getty should accumulate * before deciding that something is wrong and exit. */ #define GETTY_TIMEOUT 60 /* seconds */ /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */ #define PPP_FRAME 0x7e /* PPP Framing character */ #define PPP_STATION 0xff /* "All Station" character */ #define PPP_ESCAPE 0x7d /* Escape Character */ #define PPP_CONTROL 0x03 /* PPP Control Field */ #define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */ #define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */ #define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */ struct termios tmode, omode; int crmod, digit_or_punc, lower, upper; char hostname[MAXHOSTNAMELEN + 1]; struct utsname kerninfo; char name[LOGIN_NAME_MAX]; char dev[] = _PATH_DEV; char ttyn[32]; char lockfile[512]; uid_t ttyowner; char *rawttyn; #define OBUFSIZ 128 #define TABBUFSIZ 512 char defent[TABBUFSIZ]; char tabent[TABBUFSIZ]; char *env[128]; const unsigned char partab[] = { 0001,0201,0201,0001,0201,0001,0001,0201, 0202,0004,0003,0205,0005,0206,0201,0001, 0201,0001,0001,0201,0001,0201,0201,0001, 0001,0201,0201,0001,0201,0001,0001,0201, 0200,0000,0000,0200,0000,0200,0200,0000, 0000,0200,0200,0000,0200,0000,0000,0200, 0000,0200,0200,0000,0200,0000,0000,0200, 0200,0000,0000,0200,0000,0200,0200,0000, 0200,0000,0000,0200,0000,0200,0200,0000, 0000,0200,0200,0000,0200,0000,0000,0200, 0000,0200,0200,0000,0200,0000,0000,0200, 0200,0000,0000,0200,0000,0200,0200,0000, 0000,0200,0200,0000,0200,0000,0000,0200, 0200,0000,0000,0200,0000,0200,0200,0000, 0200,0000,0000,0200,0000,0200,0200,0000, 0000,0200,0200,0000,0200,0000,0000,0201 }; #define ERASE tmode.c_cc[VERASE] #define KILL tmode.c_cc[VKILL] #define EOT tmode.c_cc[VEOF] static void clearscreen(void); jmp_buf timeout; static void /*ARGSUSED*/ dingdong(int signo) { (void)alarm(0); (void)signal(SIGALRM, SIG_DFL); longjmp(timeout, 1); } jmp_buf intrupt; static void /*ARGSUSED*/ interrupt(int signo) { (void)signal(SIGINT, interrupt); longjmp(intrupt, 1); } /* * Action to take when getty is running too long. */ static void /*ARGSUSED*/ timeoverrun(int signo) { syslog(LOG_ERR, "getty exiting due to excessive running time"); exit(1); } static int getname(void); static void oflush(void); static void prompt(void); static void putchr(int); static void putf(const char *); static void putpad(const char *); static void xputs(const char *); int main(int argc, char *argv[], char *envp[]) { const char *progname; char *tname; int repcnt = 0, failopenlogged = 0, uugetty = 0, first_time = 1; struct rlimit limit; struct passwd *pw; int rval; (void)signal(SIGINT, SIG_IGN); openlog("getty", LOG_PID, LOG_AUTH); (void)gethostname(hostname, sizeof(hostname)); hostname[sizeof(hostname) - 1] = '\0'; if (hostname[0] == '\0') (void)strlcpy(hostname, "Amnesiac", sizeof(hostname)); (void)uname(&kerninfo); progname = getprogname(); if (progname[0] == 'u' && progname[1] == 'u') uugetty = 1; /* * Find id of uucp login (if present) so we can chown tty properly. */ if (uugetty && (pw = getpwnam("uucp"))) ttyowner = pw->pw_uid; else ttyowner = 0; /* * Limit running time to deal with broken or dead lines. */ (void)signal(SIGXCPU, timeoverrun); limit.rlim_max = RLIM_INFINITY; limit.rlim_cur = GETTY_TIMEOUT; (void)setrlimit(RLIMIT_CPU, &limit); /* * The following is a work around for vhangup interactions * which cause great problems getting window systems started. * If the tty line is "-", we do the old style getty presuming * that the file descriptors are already set up for us. * J. Gettys - MIT Project Athena. */ if (argc <= 2 || strcmp(argv[2], "-") == 0) { (void)strlcpy(ttyn, ttyname(0), sizeof(ttyn)); } else { int i; rawttyn = argv[2]; (void)strlcpy(ttyn, dev, sizeof(ttyn)); (void)strlcat(ttyn, argv[2], sizeof(ttyn)); if (uugetty) { (void)chown(ttyn, ttyowner, 0); (void)strlcpy(lockfile, _PATH_LOCK, sizeof(lockfile)); (void)strlcat(lockfile, argv[2], sizeof(lockfile)); /* * wait for lockfiles to go away before we try * to open */ if (pidlock(lockfile, 0, 0, 0) != 0) { syslog(LOG_ERR, "%s: can't create lockfile", ttyn); exit(1); } (void)unlink(lockfile); } if (strcmp(argv[0], "+") != 0) { (void)chown(ttyn, ttyowner, 0); (void)chmod(ttyn, 0600); (void)revoke(ttyn); if (ttyaction(ttyn, "getty", "root")) syslog(LOG_WARNING, "%s: ttyaction failed", ttyn); /* * Delay the open so DTR stays down long enough * to be detected. */ (void)sleep(2); while ((i = open(ttyn, O_RDWR)) == -1) { if ((repcnt % 10 == 0) && (errno != ENXIO || !failopenlogged)) { syslog(LOG_WARNING, "%s: %m", ttyn); closelog(); failopenlogged = 1; } repcnt++; (void)sleep(60); } if (uugetty && pidlock(lockfile, 0, 0, 0) != 0) { syslog(LOG_ERR, "%s: can't create lockfile", ttyn); exit(1); } if (uugetty) (void)chown(lockfile, ttyowner, 0); (void)login_tty(i); } } /* Start with default tty settings */ if (tcgetattr(0, &tmode) < 0) { syslog(LOG_ERR, "%s: %m", ttyn); exit(1); } omode = tmode; gettable("default", defent); gendefaults(); tname = "default"; if (argc > 1) tname = argv[1]; for (;;) { int off; rval = 0; gettable(tname, tabent); if (OPset || EPset || APset) APset++, OPset++, EPset++; setdefaults(); off = 0; (void)tcflush(0, TCIOFLUSH); /* clear out the crap */ (void)ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */ (void)ioctl(0, FIOASYNC, &off); /* ditto for async mode */ if (IS) (void)cfsetispeed(&tmode, (speed_t)IS); else if (SP) (void)cfsetispeed(&tmode, (speed_t)SP); if (OS) (void)cfsetospeed(&tmode, (speed_t)OS); else if (SP) (void)cfsetospeed(&tmode, (speed_t)SP); setflags(0); setchars(); if (tcsetattr(0, TCSANOW, &tmode) < 0) { syslog(LOG_ERR, "%s: %m", ttyn); exit(1); } if (AB) { tname = autobaud(); continue; } if (PS) { tname = portselector(); continue; } if (CS) clearscreen(); if (CL && *CL) putpad(CL); edithost(HE); /* * If this is the first time through this, and an * issue file has been given, then send it. */ if (first_time != 0 && IF != NULL) { char buf[_POSIX2_LINE_MAX]; FILE *fp; if ((fp = fopen(IF, "r")) != NULL) { while (fgets(buf, sizeof(buf) - 1, fp) != NULL) putf(buf); (void)fclose(fp); } } first_time = 0; if (IM && *IM) putf(IM); oflush(); if (setjmp(timeout)) { tmode.c_ispeed = tmode.c_ospeed = 0; (void)tcsetattr(0, TCSANOW, &tmode); exit(1); } if (TO) { (void)signal(SIGALRM, dingdong); (void)alarm((unsigned int)TO); } if (NN) { name[0] = '\0'; lower = 1; upper = digit_or_punc = 0; } else if (AL) { const char *p = AL; char *q = name; while (*p && q < &name[sizeof name - 1]) { if (isupper((unsigned char)*p)) upper = 1; else if (islower((unsigned char)*p)) lower = 1; else if (isdigit((unsigned char)*p)) digit_or_punc = 1; *q++ = *p++; } } else if ((rval = getname()) == 2) { setflags(2); (void)execle(PP, "ppplogin", ttyn, (char *) 0, env); syslog(LOG_ERR, "%s: %m", PP); exit(1); } if (rval || AL || NN) { int i; oflush(); (void)alarm(0); (void)signal(SIGALRM, SIG_DFL); if (name[0] == '-') { xputs("user names may not start with '-'."); continue; } if (!(upper || lower || digit_or_punc)) continue; setflags(2); if (crmod) { tmode.c_iflag |= ICRNL; tmode.c_oflag |= ONLCR; } #if XXX if (upper || UC) tmode.sg_flags |= LCASE; if (lower || LC) tmode.sg_flags &= ~LCASE; #endif if (tcsetattr(0, TCSANOW, &tmode) < 0) { syslog(LOG_ERR, "%s: %m", ttyn); exit(1); } (void)signal(SIGINT, SIG_DFL); for (i = 0; envp[i] != NULL; i++) env[i] = envp[i]; makeenv(&env[i]); limit.rlim_max = RLIM_INFINITY; limit.rlim_cur = RLIM_INFINITY; (void)setrlimit(RLIMIT_CPU, &limit); if (NN) (void)execle(LO, "login", AL ? "-fp" : "-p", NULL, env); else (void)execle(LO, "login", AL ? "-fp" : "-p", "--", name, NULL, env); syslog(LOG_ERR, "%s: %m", LO); exit(1); } (void)alarm(0); (void)signal(SIGALRM, SIG_DFL); (void)signal(SIGINT, SIG_IGN); if (NX && *NX) tname = NX; if (uugetty) (void)unlink(lockfile); } } static int getname(void) { int c; char *np; unsigned char cs; int ppp_state, ppp_connection; /* * Interrupt may happen if we use CBREAK mode */ if (setjmp(intrupt)) { (void)signal(SIGINT, SIG_IGN); return (0); } (void)signal(SIGINT, interrupt); setflags(1); prompt(); if (PF > 0) { oflush(); (void)sleep((unsigned int)PF); PF = 0; } if (tcsetattr(0, TCSANOW, &tmode) < 0) { syslog(LOG_ERR, "%s: %m", ttyn); exit(1); } crmod = digit_or_punc = lower = upper = 0; ppp_state = ppp_connection = 0; np = name; for (;;) { oflush(); if (read(STDIN_FILENO, &cs, 1) <= 0) exit(0); if ((c = cs&0177) == 0) return (0); /* * PPP detection state machine.. * Look for sequences: * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC) * See RFC1662. * Derived from code from Michael Hancock * and Erik 'PPP' Olson */ if (PP && cs == PPP_FRAME) { ppp_state = 1; } else if (ppp_state == 1 && cs == PPP_STATION) { ppp_state = 2; } else if (ppp_state == 2 && cs == PPP_ESCAPE) { ppp_state = 3; } else if ((ppp_state == 2 && cs == PPP_CONTROL) || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) { ppp_state = 4; } else if (ppp_state == 4 && cs == PPP_LCP_HI) { ppp_state = 5; } else if (ppp_state == 5 && cs == PPP_LCP_LOW) { ppp_connection = 1; break; } else { ppp_state = 0; } if (c == EOT) exit(1); if (c == '\r' || c == '\n' || np >= &name[LOGIN_NAME_MAX - 1]) { *np = '\0'; putf("\r\n"); break; } if (islower(c)) lower = 1; else if (isupper(c)) upper = 1; else if (c == ERASE || c == '#' || c == '\b') { if (np > name) { np--; if (cfgetospeed(&tmode) >= 1200) xputs("\b \b"); else putchr(cs); } continue; } else if (c == KILL || c == '@') { putchr(cs); putchr('\r'); if (cfgetospeed(&tmode) < 1200) putchr('\n'); /* this is the way they do it down under ... */ else if (np > name) xputs( " \r"); prompt(); np = name; continue; } else if (isdigit(c) || c == '_') digit_or_punc = 1; if (IG && (c <= ' ' || c > 0176)) continue; *np++ = c; putchr(cs); /* * An MS-Windows direct connect PPP "client" won't send its * first PPP packet until we respond to its "CLIENT" poll * with a CRLF sequence. We cater to yet another broken * implementation of a previously-standard protocol... */ *np = '\0'; if (strstr(name, "CLIENT")) putf("\r\n"); } (void)signal(SIGINT, SIG_IGN); *np = 0; if (c == '\r') crmod = 1; if ((upper && !lower && !LC) || UC) for (np = name; *np; np++) *np = tolower((unsigned char)*np); return (1 + ppp_connection); } static void putpad(const char *s) { int pad = 0; speed_t ospd = cfgetospeed(&tmode); if (isdigit((unsigned char)*s)) { while (isdigit((unsigned char)*s)) { pad *= 10; pad += *s++ - '0'; } pad *= 10; if (*s == '.' && isdigit((unsigned char)s[1])) { pad += s[1] - '0'; s += 2; } } xputs(s); /* * If no delay needed, or output speed is * not comprehensible, then don't try to delay. */ if (pad == 0) return; /* * Round up by a half a character frame, and then do the delay. * Too bad there are no user program accessible programmed delays. * Transmitting pad characters slows many terminals down and also * loads the system. */ pad = (pad * ospd + 50000) / 100000; while (pad--) putchr(*PC); } static void xputs(const char *s) { while (*s) putchr(*s++); } char outbuf[OBUFSIZ]; size_t obufcnt = 0; static void putchr(int cc) { unsigned char c; c = cc; if (!NP) { c |= partab[c&0177] & 0200; if (OP) c ^= 0200; } if (!UB) { outbuf[obufcnt++] = c; if (obufcnt >= OBUFSIZ) oflush(); } else (void)write(STDOUT_FILENO, &c, 1); } static void oflush(void) { if (obufcnt) (void)write(STDOUT_FILENO, outbuf, obufcnt); obufcnt = 0; } static void prompt(void) { putf(LM); if (CO) putchr('\n'); } static void putf(const char *cp) { time_t t; char *slash, db[100]; while (*cp) { if (*cp != '%') { putchr(*cp++); continue; } switch (*++cp) { case 't': if ((slash = strstr(ttyn, "/pts/")) == NULL) slash = strrchr(ttyn, '/'); if (slash == NULL) xputs(ttyn); else xputs(&slash[1]); break; case 'h': xputs(editedhost); break; case 'd': (void)time(&t); (void)strftime(db, sizeof(db), "%l:%M%p on %A, %d %B %Y", localtime(&t)); xputs(db); break; case 's': xputs(kerninfo.sysname); break; case 'm': xputs(kerninfo.machine); break; case 'r': xputs(kerninfo.release); break; case 'v': xputs(kerninfo.version); break; case '%': putchr('%'); break; } if (*cp) cp++; } } static void clearscreen(void) { struct ttyent *typ; int err; if (rawttyn == NULL) return; typ = getttynam(rawttyn); if ((typ == NULL) || (typ->ty_type == NULL) || (typ->ty_type[0] == 0)) return; if (setupterm(typ->ty_type, 0, &err) == ERR) return; if (clear_screen) putpad(clear_screen); del_curterm(cur_term); cur_term = NULL; }