📄 getty.c
字号:
/* vi: set sw=4 ts=4: *//* agetty.c - another getty program for Linux. By W. Z. Venema 1989 Ported to Linux by Peter Orbaek <poe@daimi.aau.dk> This program is freely distributable. The entire man-page used to be here. Now read the real man-page agetty.8 instead. -f option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95 1999-02-22 Arkadiusz Mi秌iewicz <misiek@misiek.eu.org> - added Native Language Support 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net> - enable hardware flow control before displaying /etc/issue */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/ioctl.h>#include <errno.h>#include <sys/stat.h>#include <sys/signal.h>#include <fcntl.h>#include <stdarg.h>#include <ctype.h>#include <utmp.h>#include <getopt.h>#include <termios.h>#include "busybox.h"#define _PATH_LOGIN "/bin/login"#ifdef linux#include <sys/param.h>#define USE_SYSLOG#endifextern void updwtmp(const char *filename, const struct utmp *ut); /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */#ifdef USE_SYSLOG#include <syslog.h>#endif /* * Some heuristics to find out what environment we are in: if it is not * System V, assume it is SunOS 4. */#ifdef LOGIN_PROCESS /* defined in System V utmp.h */#define SYSV_STYLE /* select System V style getty */#endif /* * Things you may want to modify. * * If ISSUE is not defined, agetty will never display the contents of the * /etc/issue file. You will not want to spit out large "issue" files at the * wrong baud rate. Relevant for System V only. * * You may disagree with the default line-editing etc. characters defined * below. Note, however, that DEL cannot be used for interrupt generation * and for line editing at the same time. */#ifdef SYSV_STYLE#define ISSUE "/etc/issue" /* displayed before the login prompt */#include <sys/utsname.h>#include <time.h>#endif#define LOGIN " login: " /* login prompt *//* Some shorthands for control characters. */#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */#define CR CTL('M') /* carriage return */#define NL CTL('J') /* line feed */#define BS CTL('H') /* back space */#define DEL CTL('?') /* delete *//* Defaults for line-editing etc. characters; you may want to change this. */#define DEF_ERASE DEL /* default erase character */#define DEF_INTR CTL('C') /* default interrupt character */#define DEF_QUIT CTL('\\') /* default quit char */#define DEF_KILL CTL('U') /* default kill char */#define DEF_EOF CTL('D') /* default EOF char */#define DEF_EOL 0#define DEF_SWITCH 0 /* default switch char */ /* * SunOS 4.1.1 termio is broken. We must use the termios stuff instead, * because the termio -> termios translation does not clear the termios * CIBAUD bits. Therefore, the tty driver would sometimes report that input * baud rate != output baud rate. I did not notice that problem with SunOS * 4.1. We will use termios where available, and termio otherwise. *//* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set properly, but all is well if we use termios?! */#ifdef TCGETS#undef TCGETA#undef TCSETA#undef TCSETAW#define termio termios#define TCGETA TCGETS#define TCSETA TCSETS#define TCSETAW TCSETSW#endif /* * This program tries to not use the standard-i/o library. This keeps the * executable small on systems that do not have shared libraries (System V * Release <3). */#ifndef BUFSIZ#define BUFSIZ 1024#endif /* * When multiple baud rates are specified on the command line, the first one * we will try is the first one specified. */#define FIRST_SPEED 0/* Storage for command-line options. */#define MAX_SPEED 10 /* max. nr. of baud rates */struct options { int flags; /* toggle switches, see below */ int timeout; /* time-out period */ char *login; /* login program */ char *tty; /* name of tty */ char *initstring; /* modem init string */ char *issue; /* alternative issue file */ int numspeed; /* number of baud rates to try */ int speeds[MAX_SPEED]; /* baud rates to be tried */};#define F_PARSE (1<<0) /* process modem status messages */#define F_ISSUE (1<<1) /* display /etc/issue */#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */#define F_LOCAL (1<<3) /* force local */#define F_INITSTRING (1<<4) /* initstring is set */#define F_WAITCRLF (1<<5) /* wait for CR or LF */#define F_CUSTISSUE (1<<6) /* give alternative issue file */#define F_NOPROMPT (1<<7) /* don't ask for login name! *//* Storage for things detected while the login name was read. */struct chardata { int erase; /* erase character */ int kill; /* kill character */ int eol; /* end-of-line character */ int parity; /* what parity did we see */ int capslock; /* upper case without lower case */};/* Initial values for the above. */struct chardata init_chardata = { DEF_ERASE, /* default erase character */ DEF_KILL, /* default kill character */ 13, /* default eol char */ 0, /* space parity */ 0, /* no capslock */};struct Speedtab { long speed; int code;};static struct Speedtab speedtab[] = { {50, B50}, {75, B75}, {110, B110}, {134, B134}, {150, B150}, {200, B200}, {300, B300}, {600, B600}, {1200, B1200}, {1800, B1800}, {2400, B2400}, {4800, B4800}, {9600, B9600},#ifdef B19200 {19200, B19200},#endif#ifdef B38400 {38400, B38400},#endif#ifdef EXTA {19200, EXTA},#endif#ifdef EXTB {38400, EXTB},#endif#ifdef B57600 {57600, B57600},#endif#ifdef B115200 {115200, B115200},#endif#ifdef B230400 {230400, B230400},#endif {0, 0},};static void parse_args(int argc, char **argv, struct options *op);static void parse_speeds(struct options *op, char *arg);static void update_utmp(char *line);static void open_tty(char *tty, struct termio *tp, int local);static void termio_init(struct termio *tp, int speed, struct options *op);static void auto_baud(struct termio *tp);static void do_prompt(struct options *op, struct termio *tp);static void next_speed(struct termio *tp, struct options *op);static char *get_logname(struct options *op, struct chardata *cp, struct termio *tp);static void termio_final(struct options *op, struct termio *tp, struct chardata *cp);static int caps_lock(const char *s);static int bcode(const char *s);static void error(const char *fmt, ...);/* The following is used for understandable diagnostics. *//* Fake hostname for ut_host specified on command line. */static char *fakehost = NULL;/* ... */#ifdef DEBUGGING#define debug(s) fprintf(dbf,s); fflush(dbf)#define DEBUGTERM "/dev/ttyp0"FILE *dbf;#else#define debug(s) /* nothing */#endifint getty_main(int argc, char **argv){ char *logname = NULL; /* login name, given to /bin/login */ struct chardata chardata; /* set by get_logname() */ struct termio termio; /* terminal mode bits */ static struct options options = { F_ISSUE, /* show /etc/issue (SYSV_STYLE) */ 0, /* no timeout */ _PATH_LOGIN, /* default login program */ "tty1", /* default tty line */ "", /* modem init string */ ISSUE, /* default issue file */ 0, /* no baud rates known yet */ };#ifdef DEBUGGING dbf = xfopen(DEBUGTERM, "w"); { int i; for (i = 1; i < argc; i++) { debug(argv[i]); debug("\n"); } }#endif /* Parse command-line arguments. */ parse_args(argc, argv, &options);#ifdef __linux__ setsid();#endif /* Update the utmp file. */#ifdef SYSV_STYLE update_utmp(options.tty);#endif debug("calling open_tty\n"); /* Open the tty as standard { input, output, error }. */ open_tty(options.tty, &termio, options.flags & F_LOCAL);#ifdef __linux__ { int iv; iv = getpid(); if (ioctl(0, TIOCSPGRP, &iv) < 0) perror_msg("ioctl() TIOCSPGRP call failed"); }#endif /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */ debug("calling termio_init\n"); termio_init(&termio, options.speeds[FIRST_SPEED], &options); /* write the modem init string and DON'T flush the buffers */ if (options.flags & F_INITSTRING) { debug("writing init string\n"); write(1, options.initstring, strlen(options.initstring)); } if (!(options.flags & F_LOCAL)) { /* go to blocking write mode unless -L is specified */ fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK); } /* Optionally detect the baud rate from the modem status message. */ debug("before autobaud\n"); if (options.flags & F_PARSE) auto_baud(&termio); /* Set the optional timer. */ if (options.timeout) (void) alarm((unsigned) options.timeout); /* optionally wait for CR or LF before writing /etc/issue */ if (options.flags & F_WAITCRLF) { char ch; debug("waiting for cr-lf\n"); while (read(0, &ch, 1) == 1) { ch &= 0x7f; /* strip "parity bit" */#ifdef DEBUGGING fprintf(dbf, "read %c\n", ch);#endif if (ch == '\n' || ch == '\r') break; } } chardata = init_chardata; if (!(options.flags & F_NOPROMPT)) { /* Read the login name. */ debug("reading login name\n"); /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */ while ((logname = get_logname(&options, &chardata, &termio)) == NULL) next_speed(&termio, &options); } /* Disable timer. */ if (options.timeout) (void) alarm(0); /* Finalize the termio settings. */ termio_final(&options, &termio, &chardata); /* Now the newline character should be properly written. */ (void) write(1, "\n", 1); /* Let the login program take care of password validation. */ (void) execl(options.login, options.login, "--", logname, (char *) 0); error("%s: can't exec %s: %m", options.tty, options.login); return (0); /* quiet GCC */}/* parse-args - parse command-line arguments */static void parse_args(int argc, char **argv, struct options *op){ extern char *optarg; /* getopt */ extern int optind; /* getopt */ int c; while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) { switch (c) { case 'I': if (!(op->initstring = strdup(optarg))) { error("can't malloc initstring"); break; } { char ch, *p, *q; int i; /* copy optarg into op->initstring decoding \ddd octal codes into chars */ q = op->initstring; p = optarg; while (*p) { if (*p == '\\') { /* know \\ means \ */ p++; if (*p == '\\') { ch = '\\'; p++; } else { /* handle \000 - \177 */ ch = 0; for (i = 1; i <= 3; i++) { if (*p >= '0' && *p <= '7') { ch <<= 3; ch += *p - '0'; p++; } else break; } } *q++ = ch; } else { *q++ = *p++; } } *q = '\0'; } op->flags |= F_INITSTRING; break; case 'L': /* force local */ op->flags |= F_LOCAL; break; case 'H': /* fake login host */ fakehost = optarg; break; case 'f': /* custom issue file */ op->flags |= F_CUSTISSUE; op->issue = optarg; break; case 'h': /* enable h/w flow control */ op->flags |= F_RTSCTS; break; case 'i': /* do not show /etc/issue */ op->flags &= ~F_ISSUE; break; case 'l': op->login = optarg; /* non-default login program */ break; case 'm': /* parse modem status message */ op->flags |= F_PARSE; break; case 'n': op->flags |= F_NOPROMPT; break; case 't': /* time out */ if ((op->timeout = atoi(optarg)) <= 0) error("bad timeout value: %s", optarg); break; case 'w': op->flags |= F_WAITCRLF; break; default: show_usage(); } } debug("after getopt loop\n"); if (argc < optind + 2) /* check parameter count */ show_usage(); /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ if ('0' <= argv[optind][0] && argv[optind][0] <= '9') { /* a number first, assume it's a speed (BSD style) */ parse_speeds(op, argv[optind++]); /* baud rate(s) */ op->tty = argv[optind]; /* tty name */ } else { op->tty = argv[optind++]; /* tty name */ parse_speeds(op, argv[optind]); /* baud rate(s) */ } optind++; if (argc > optind && argv[optind]) setenv("TERM", argv[optind], 1); debug("exiting parseargs\n");}/* parse_speeds - parse alternate baud rates */static void parse_speeds(struct options *op, char *arg){ char *cp; debug("entered parse_speeds\n"); for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) { if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) error("bad speed: %s", cp); if (op->numspeed > MAX_SPEED) error("too many alternate speeds"); } debug("exiting parsespeeds\n");}#ifdef SYSV_STYLE/* update_utmp - update our utmp entry */static void update_utmp(char *line){ struct utmp ut; struct utmp *utp; time_t t; int mypid = getpid();#if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)) struct flock lock;#endif /* * The utmp file holds miscellaneous information about things started by * /sbin/init and other system-related events. Our purpose is to update * the utmp entry for the current process, in particular the process type * and the tty line we are listening to. Return successfully only if the * utmp file can be opened for update, and if we are able to find our * entry in the utmp file. */ utmpname(_PATH_UTMP); setutent(); while ((utp = getutent()) && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) /* nothing */ ; if (utp) { memcpy(&ut, utp, sizeof(ut)); } else { /* some inits don't initialize utmp... */ memset(&ut, 0, sizeof(ut)); strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); } /*endutent(); */ strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user)); strncpy(ut.ut_line, line, sizeof(ut.ut_line)); if (fakehost) strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); time(&t); ut.ut_time = t; ut.ut_type = LOGIN_PROCESS; ut.ut_pid = mypid; pututline(&ut); endutent(); { updwtmp(_PATH_WTMP, &ut); }}#endif/* open_tty - set up tty as standard { input, output, error } */static void open_tty(char *tty, struct termio *tp, int local){ /* Get rid of the present standard { output, error} if any. */ (void) close(1); (void) close(2); errno = 0; /* ignore above errors */ /* Set up new standard input, unless we are given an already opened port. */ if (strcmp(tty, "-")) { struct stat st; /* Sanity checks... */ if (chdir("/dev")) error("/dev: chdir() failed: %m"); if (stat(tty, &st) < 0) error("/dev/%s: %m", tty); if ((st.st_mode & S_IFMT) != S_IFCHR) error("/dev/%s: not a character device", tty); /* Open the tty as standard input. */ (void) close(0); errno = 0; /* ignore close(2) errors */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -