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 <werner@xxxxxxx> --- term-utils/agetty.8 | 3 + term-utils/agetty.c | 144 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 113 insertions(+), 34 deletions(-) diff --git a/term-utils/agetty.8 b/term-utils/agetty.8 index 21a07a0..d30f7ce 100644 --- a/term-utils/agetty.8 +++ b/term-utils/agetty.8 @@ -172,6 +172,9 @@ Wait for the user or the modem to send a carriage-return or a linefeed character before sending the \fI/etc/issue\fP (or other) file and the login prompt. Very useful in connection with the \-I option. .TP +\-\-nohangup +Do not call vhangup() for a virtually hangup of the specified terminal. +.TP \-\-version Output version information and exit. .TP diff --git a/term-utils/agetty.c b/term-utils/agetty.c index 35ef1c2..6ce7abd 100644 --- a/term-utils/agetty.c +++ b/term-utils/agetty.c @@ -16,6 +16,7 @@ #include <termios.h> #include <signal.h> #include <errno.h> +#include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -28,6 +29,7 @@ #include <sys/socket.h> #include <netdb.h> #include <langinfo.h> +#include <grp.h> #include "strutils.h" #include "nls.h" @@ -146,6 +148,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_NOHANGUP (1<<13) /* No not call vhangup(2) */ /* Storage for things detected while the login name was read. */ struct chardata { @@ -258,11 +262,20 @@ 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); + 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++) @@ -272,10 +285,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); @@ -286,6 +295,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"); @@ -295,7 +310,7 @@ int main(int argc, char **argv) if (options.flags & F_INITSTRING) { debug("writing init string\n"); safe_write(STDIN_FILENO, options.initstring, - strlen(options.initstring)); + strlen(options.initstring)); } if (!(options.flags & F_LOCAL)) @@ -365,7 +380,8 @@ 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[] = { + { "nohangup", no_argument, &nipple, F_NOHANGUP }, { "8bits", no_argument, 0, '8' }, { "noreset", no_argument, 0, 'c' }, { "issue-file", required_argument, 0, 'f' }, @@ -634,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)) + error(_("/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) + warn("%s: %m", buf); + else + error("%s: %m", buf); + } + + /* Open the tty as standard input. */ + if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0) + error(_("/dev/%s: cannot open as standard input: %m"), tty); - /* Sanity checks. */ - if (chdir("/dev")) - error(_("/dev: chdir() failed: %m")); - if (stat(tty, &st) < 0) - error("/dev/%s: %m", tty); + /* Sanity checks... */ + if (!isatty (fd)) + error(_("/dev/%s: not a character device"), tty); + if (fstat(fd, &st) < 0) + error ("%s: %m", buf); if ((st.st_mode & S_IFMT) != S_IFCHR) error(_("/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) + error("/dev/%s: cannot get controlling tty: %m", tty); + } + + if ((op->flags & F_NOHANGUP) == 0) { + /* + * 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()) + error("/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) - error(_("/dev/%s: cannot open as standard input: %m"), - tty); + if (open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0) != 0) + error(_("/dev/%s: cannot open as standard input: %m"), tty); + if (((tid = tcgetsid(0)) < 0) || (pid != tid)) { + if (ioctl (0, TIOCSCTTY, 1) == -1) + error("/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) error(_("%s: not open for read/write"), tty); + } + if (tcsetpgrp(STDIN_FILENO, pid)) + error("/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"); @@ -692,12 +766,16 @@ static void open_tty(char *tty, struct termios *tp, struct options *op) error("%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(0, 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); } @@ -825,17 +903,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. */ safe_write(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. */ -- 1.6.0.2 -- To unsubscribe from this list: send the line "unsubscribe util-linux" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html