From 3aa6b68f7e19fa3e1c2bba75bee921a98b7b46af Mon Sep 17 00:00:00 2001 From: Werner Fink Date: Mon, 9 May 2011 15:52:36 +0200 Subject: agetty: proper session on the terminal line Ensure a proper session on the terminal line, that is do a vhangup() and become the controlling terminal. After this determine if the terminal line a virtual console by using the ioctl TIOCMGET to get the status modem bits of a serial line which is a invalid argument on a virtual console. Signed-off-by: Werner Fink --- term-utils/agetty.c | 158 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 123 insertions(+), 35 deletions(-) (limited to 'term-utils/agetty.c') diff --git a/term-utils/agetty.c b/term-utils/agetty.c index 5ff805f51..0d718a81d 100644 --- a/term-utils/agetty.c +++ b/term-utils/agetty.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include "strutils.h" #include "writeall.h" @@ -147,6 +149,8 @@ struct options { #define F_KEEPSPEED (1<<9) /* follow baud rate from kernel */ #define F_KEEPCFLAGS (1<<10) /* reuse c_cflags setup from kernel */ #define F_EIGHTBITS (1<<11) /* Assume 8bit-clean tty */ +#define F_VCONSOLE (1<<12) /* This is a virtual console */ +#define F_HANGUP (1<<13) /* Do call vhangup(2) */ /* Storage for things detected while the login name was read. */ struct chardata { @@ -256,11 +260,21 @@ int main(int argc, char **argv) 0, /* no baud rates known yet */ { 0 } }; + struct sigaction sa, sa_hup, sa_quit, sa_int; + sigset_t set; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); + /* In case vhangup(2) has to called */ + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_RESTART; + sigemptyset (&sa.sa_mask); + sigaction(SIGHUP, &sa, &sa_hup); + sigaction(SIGQUIT, &sa, &sa_quit); + sigaction(SIGINT, &sa, &sa_int); + #ifdef DEBUGGING dbf = fopen("/dev/ttyp0", "w"); for (int i = 1; i < argc; i++) @@ -270,10 +284,6 @@ int main(int argc, char **argv) /* Parse command-line arguments. */ parse_args(argc, argv, &options); -#ifdef __linux__ - setsid(); -#endif - /* Update the utmp file. */ #ifdef SYSV_STYLE update_utmp(&options); @@ -284,6 +294,12 @@ int main(int argc, char **argv) /* Open the tty as standard { input, output, error }. */ open_tty(options.tty, &termios, &options); + /* Unmask SIGHUP if inherited */ + sigemptyset(&set); + sigaddset(&set, SIGHUP); + sigprocmask(SIG_UNBLOCK, &set, NULL); + sigaction(SIGHUP, &sa_hup, NULL); + tcsetpgrp(STDIN_FILENO, getpid()); /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ debug("calling termio_init\n"); @@ -293,7 +309,7 @@ int main(int argc, char **argv) if (options.flags & F_INITSTRING) { debug("writing init string\n"); write_all(STDOUT_FILENO, options.initstring, - strlen(options.initstring)); + strlen(options.initstring)); } if (!(options.flags & F_LOCAL)) @@ -345,6 +361,9 @@ int main(int argc, char **argv) /* Now the newline character should be properly written. */ write_all(STDOUT_FILENO, "\n", 1); + sigaction (SIGQUIT, &sa_quit, NULL); + sigaction (SIGINT, &sa_int, NULL); + /* Let the login program take care of password validation. */ execl(options.login, options.login, "--", logname, NULL); log_err(_("%s: can't exec %s: %m"), options.tty, options.login); @@ -361,7 +380,7 @@ static void parse_args(int argc, char **argv, struct options *op) VERSION_OPTION = CHAR_MAX + 1, HELP_OPTION }; - static const struct option longopts[] = { + const struct option longopts[] = { { "8bits", no_argument, 0, '8' }, { "noreset", no_argument, 0, 'c' }, { "issue-file", required_argument, 0, 'f' }, @@ -373,6 +392,7 @@ static void parse_args(int argc, char **argv, struct options *op) { "local-line", no_argument, 0, 'L' }, { "extract-baud", no_argument, 0, 'm' }, { "skip-login", no_argument, 0, 'n' }, + { "hangup", no_argument, 0, 'R' }, { "keep-baud", no_argument, 0, 's' }, { "timeout", required_argument, 0, 't' }, { "detect-case", no_argument, 0, 'U' }, @@ -382,7 +402,7 @@ static void parse_args(int argc, char **argv, struct options *op) { NULL, 0, 0, 0 } }; - while ((c = getopt_long(argc, argv, "8cf:hH:iI:l:Lmnst:Uw", longopts, + while ((c = getopt_long(argc, argv, "8cf:hH:iI:l:LmnsRt:Uw", longopts, NULL)) != -1) { switch (c) { case '8': @@ -420,6 +440,9 @@ static void parse_args(int argc, char **argv, struct options *op) case 'n': op->flags |= F_NOPROMPT; break; + case 'R': + op->flags |= F_HANGUP; + break; case 's': op->flags |= F_KEEPSPEED; break; @@ -627,43 +650,101 @@ static void update_utmp(struct options *op) /* Set up tty as stdin, stdout & stderr. */ static void open_tty(char *tty, struct termios *tp, struct options *op) { - /* Get rid of the present outputs. */ - close(STDOUT_FILENO); - close(STDERR_FILENO); - errno = 0; + const pid_t pid = getpid(); + int serial; - /* - * Set up new standard input, unless we are given an already opened - * port. - */ - if (strcmp(tty, "-")) { + /* Set up new standard input, unless we are given an already opened port. */ + + if (strcmp(tty, "-") != 0) { + char buf[PATH_MAX+1]; + struct group *gr = NULL; struct stat st; + int fd, len; + pid_t tid; + gid_t gid = 0; + + /* Use tty group if available */ + if ((gr = getgrnam("tty"))) + gid = gr->gr_gid; + + if (((len = snprintf(buf, sizeof(buf), "/dev/%s", tty)) >= (int)sizeof(buf)) || (len < 0)) + log_err(_("/dev/%s: cannot open as standard input: %m"), tty); + + /* + * There is always a race between this reset and the call to + * vhangup() that s.o. can use to get access to your tty. + * Linux login(1) will change tty permissions. Use root owner and group + * with permission -rw------- for the period between getty and login. + */ + if (chown (buf, 0, gid) || chmod (buf, (gid ? 0660 : 0600))) { + if (errno == EROFS) + log_warn("%s: %m", buf); + else + log_err("%s: %m", buf); + } - /* Sanity checks. */ - if (chdir("/dev")) - log_err(_("/dev: chdir() failed: %m")); - if (stat(tty, &st) < 0) - log_err("/dev/%s: %m", tty); + /* Open the tty as standard input. */ + if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0) + log_err(_("/dev/%s: cannot open as standard input: %m"), tty); + + /* Sanity checks... */ + if (!isatty (fd)) + log_err(_("/dev/%s: not a character device"), tty); + if (fstat(fd, &st) < 0) + log_err ("%s: %m", buf); if ((st.st_mode & S_IFMT) != S_IFCHR) log_err(_("/dev/%s: not a character device"), tty); - /* Open the tty as standard input. */ + if (((tid = tcgetsid(fd)) < 0) || (pid != tid)) { + if (ioctl (fd, TIOCSCTTY, 1) == -1) + log_err("/dev/%s: cannot get controlling tty: %m", tty); + } + + if (op->flags & F_HANGUP) { + /* + * vhangup() will replace all open file descriptors in the kernel + * that point to our controlling tty by a dummy that will deny + * further reading/writing to our device. It will also reset the + * tty to sane defaults, so we don't have to modify the tty device + * for sane settings. We also get a SIGHUP/SIGCONT. + */ + if (vhangup()) + log_err("/dev/%s: vhangup() failed: %m", tty); + (void)ioctl(fd, TIOCNOTTY); + } + + (void) close(fd); close(STDIN_FILENO); errno = 0; debug("open(2)\n"); - if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0) - log_err(_("/dev/%s: cannot open as standard input: %m"), - tty); + if (open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0) != 0) + log_err(_("/dev/%s: cannot open as standard input: %m"), tty); + if (((tid = tcgetsid(STDIN_FILENO)) < 0) || (pid != tid)) { + if (ioctl (STDIN_FILENO, TIOCSCTTY, 1) == -1) + log_err("/dev/%s: cannot get controlling tty: %m", tty); + } + } else { + /* * Standard input should already be connected to an open port. Make * sure it is open for read/write. */ + if ((fcntl(STDIN_FILENO, F_GETFL, 0) & O_RDWR) != O_RDWR) log_err(_("%s: not open for read/write"), tty); + } + if (tcsetpgrp(STDIN_FILENO, pid)) + log_err("/dev/%s: cannot set process group: %m", tty); + + /* Get rid of the present outputs. */ + close(STDOUT_FILENO); + close(STDERR_FILENO); + errno = 0; + /* Set up standard output and standard error file descriptors. */ debug("duping\n"); @@ -671,6 +752,9 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) if (dup(STDIN_FILENO) != 1 || dup(STDIN_FILENO) != 2) log_err(_("%s: dup problem: %m"), tty); + /* make stdio unbuffered for slow modem lines */ + setvbuf(stdout, NULL, _IONBF, 0); + /* * The following ioctl will fail if stdin is not a tty, but also when * there is noise on the modem control lines. In the latter case, the @@ -686,12 +770,16 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) log_err("%s: tcgetattr: %m", tty); /* - * Linux login(1) will change tty permissions. Use root owner and group - * with permission -rw------- for the period between getty and login. + * Detect if this is a virtual console or serial/modem line. + * In case of a virtual console the ioctl TIOCMGET fails and + * the error number will be set to EINVAL. */ - ignore_result(chown(tty, 0, 0)); - ignore_result(chmod(tty, 0600)); - errno = 0; + if (ioctl(STDIN_FILENO, TIOCMGET, &serial) < 0 && (errno = EINVAL)) { + op->flags |= F_VCONSOLE; + if (!op->term) + op->term = DEFAULT_VCTERM; + } else if (!op->term) + op->term = DEFAULT_STERM; setenv("TERM", op->term, 1); } @@ -819,17 +907,15 @@ static void do_prompt(struct options *op, struct termios *tp) { #ifdef ISSUE FILE *fd; - int oflag; - int c; - struct utsname uts; - - uname(&uts); #endif /* ISSUE */ /* Issue not in use, start with a new line. */ write_all(STDOUT_FILENO, "\r\n", 2); + #ifdef ISSUE if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) { + int c, oflag; + /* Save current setting. */ oflag = tp->c_oflag; /* Map new line in output to carriage return & new line. */ @@ -1113,6 +1199,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out) " -L, --local-line force local line\n" " -m, --extract-baud extract baud rate during connect\n" " -n, --skip-login do not prompt for login\n" + " -R, --hangup do virtually hangup on the tty\n" " -s, --keep-baud try to keep baud rate after break\n" " -t, --timeout NUMBER login process timeout\n" " -U, --detect-case detect uppercase terminal\n" @@ -1197,6 +1284,7 @@ static void output_special_char(unsigned char c, struct options *op, { struct utsname uts; (void) uname(&uts); + switch (c) { case 's': (void) printf ("%s", uts.sysname); -- cgit v1.2.3-55-g7522