diff options
-rw-r--r-- | daemons/runttys.c | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/daemons/runttys.c b/daemons/runttys.c new file mode 100644 index 00000000..7da9e9c2 --- /dev/null +++ b/daemons/runttys.c @@ -0,0 +1,471 @@ +/* /etc/ttys support for Hurd + Copyright (C) 1993,94,95,96,97,98,99 Free Software Foundation, Inc. + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + The GNU Hurd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <argz.h> +#include <assert.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <paths.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <ttyent.h> +#include <unistd.h> +#include <utmp.h> + + +/* How long to wait after starting window specs before starting getty */ +#define WINDOW_DELAY 3 /* seconds */ + +#define _PATH_LOGIN "/bin/login" + + +/* All the ttys in /etc/ttys. */ +struct terminal +{ + char *name; /* Name of the terminal device file. */ + + /* argv lists for getty and window spec. + The first element is always the malloc'd argz the rest point into. */ + char **getty_argv, **window_argv; + + int on; /* Nonzero iff the line is "on". */ + pid_t pid; /* Child running on this line. */ + int read; /* Used during reread_ttys. */ +}; + +static struct terminal *ttys; +/* Number of live elements in ttys */ +static int nttys; +/* Total number of elements in ttys */ +static int ttyslen; + + +static void +free_argvs (struct terminal *t) +{ + if (t->getty_argv) + { + free (t->getty_argv[0]); + free (t->getty_argv); + } + if (t->window_argv) + { + free (t->window_argv[0]); + free (t->window_argv); + } +} + +/* Set up the getty and window fields of terminal spec T corresponding + to line TT. */ +static void +setup_terminal (struct terminal *t, struct ttyent *tt) +{ + free_argvs (t); + + if ((tt->ty_status & TTY_ON) && tt->ty_getty) + { + char **make_args (const char *line) + { + int argc; + char *argz, **argv; + size_t len; + argz_create_sep (line, ' ', &argz, &len); + argc = argz_count (argz, len); + argv = malloc (argc * sizeof (char *)); + argz_extract (argz, len, argv); + return argv; + } + + char *line; + asprintf (&line, "%s %s", tt->ty_getty, tt->ty_name); + t->getty_argv = make_args (line); + free (line); + t->window_argv = tt->ty_window ? make_args (tt->ty_window) : 0; + } + else + t->getty_argv = t->window_argv = 0; +} + + +/* Add a new terminal spec for TT and return it. */ +static struct terminal * +add_terminal (struct ttyent *tt) +{ + struct terminal *t; + + if (nttys >= ttyslen) + { + ttys = realloc (ttys, (ttyslen * 2) * sizeof (struct ttyent)); + memset (&ttys[nttys], 0, ttyslen); + ttyslen *= 2; + } + + t = &ttys[nttys]; + nttys++; + + t->name = strdup (tt->ty_name); + + setup_terminal (t, tt); + if (t->getty_argv) + t->on = 1; + + return t; +} + +/* Read /etc/ttys and initialize ttys array. Return non-zero if we fail. */ +int +init_ttys (void) +{ + struct ttyent *tt; + + ttyslen = 10; + nttys = 0; + + ttys = calloc (ttyslen, sizeof (struct ttyent)); + + if (!setttyent ()) + { + error (0, errno, "%s", _PATH_TTYS); + return 1; + } + while ((tt = getttyent ())) + { + if (!tt->ty_name) + continue; + + add_terminal (tt); + } + + endttyent (); + return 0; +} + +/* Free everyting in the terminal array */ +void +free_ttys (void) +{ + int i; + + for (i = 0; i < nttys; i++) + { + free_argvs (&ttys[i]); + free (ttys[i].name); + } + free (ttys); +} + +/* Start a child process. */ +static pid_t +run (char **argv, int do_setsid) +{ + pid_t pid; + + pid = fork (); + if (pid < 0) + { + error (0, errno, "fork"); + return 0; + } + + if (pid > 0) + return pid; + else + { + if (do_setsid && setsid ()) + error (0, errno, "setsid"); + + errno = 0; + execv (argv[0], argv); + error (127, errno, "%s", argv[0]); + } + + /* NOTREACHED */ + return -1; +} + + +/* Start line T. Return non-zero if we didn't actually start anything. */ +static int +startup_terminal (struct terminal *t) +{ + pid_t pid; + assert (t->on); + assert (t->getty_argv); + + if (t->window_argv) + { + pid = run (t->window_argv, 1); + if (!pid) + goto error; + + sleep (WINDOW_DELAY); + } + + pid = run (t->getty_argv, 0); + if (pid == 0) + { + error: + t->pid = 0; + t->on = 0; + return 1; + } + else + { + t->pid = pid; + return 0; + } +} + +/* For each line in /etc/ttys, start up the specified program. Return + non-zero if we fail. */ +int +startup_ttys (void) +{ + int i; + int didone, fail; + + didone = 0; + + for (i = 0; i < nttys; i++) + if (ttys[i].on) + { + fail = startup_terminal (&ttys[i]); + if (!fail) + didone = 1; + } + return !didone; +} + +/* Find the terminal spec corresponding to line LINE. */ +static struct terminal * +find_line (char *line) +{ + int i; + + for (i = 0; i < nttys; i++) + if (!strcmp (ttys[i].name, line)) + return &ttys[i]; + return 0; +} + +/* PID has just exited; restart the terminal it's on if necessary. */ +void +restart_terminal (pid_t pid) +{ + int i; + + for (i = 0; i < nttys; i++) + if (pid == ttys[i].pid) + { + if (logout (ttys[i].name)) + logwtmp (ttys[i].name, "", ""); + ttys[i].pid = 0; + if (ttys[i].on) + startup_terminal (&ttys[i]); + } +} + +/* Shutdown the things running on terminal spec T. */ +static void +shutdown_terminal (struct terminal *t) +{ + kill (t->pid, SIGHUP); + revoke (t->name); +} + +/* Re-read /etc/ttys. If a line has turned off, kill what's there. + If a line has turned on, start it. */ +void +reread_ttys (void) +{ + struct ttyent *tt; + struct terminal *t; + int on; + int i; + + if (!setttyent ()) + { + error (0, errno, "%s", _PATH_TTYS); + return; + } + + while ((tt = getttyent ())) + { + if (!tt->ty_name) + continue; + + t = find_line (tt->ty_name); + on = tt->ty_getty && (tt->ty_status & TTY_ON); + + if (t) + { + if (t->on && !on) + { + t->on = 0; + shutdown_terminal (t); + } + else if (!t->on && on) + { + t->on = 1; + setup_terminal (t, tt); + startup_terminal (t); + } + } + else + { + t = add_terminal (tt); + if (on) + startup_terminal (t); + } + + t->read = 1; + } + endttyent (); + + /* Scan tty entries; any that were not found and were on, turn off. */ + for (i = 0; i < nttys; i++) + { + if (!ttys[i].read && ttys[i].on) + { + ttys[i].on = 0; + shutdown_terminal (&ttys[i]); + } + ttys[i].read = 0; /* Clear flag for next time. */ + } +} + + + +/** Main program and signal handlers. **/ + +static sig_atomic_t pending_hup; +static void +sighup (int signo) +{ + pending_hup = 1; +} + +static sig_atomic_t pending_term; +static void +sigterm (int signo) +{ + pending_term = 1; +} + +#ifdef SIGLOST +static void +reopen_console (int signo) +{ + int fd; + + close (0); + close (1); + close (2); + + fd = open (_PATH_CONSOLE, O_RDWR); + if (fd < 0) + _exit (2); + if (fd != 0) + { + dup2 (fd, 0); + close (fd); + } + dup2 (0, 1); + dup2 (0, 2); +} +#endif + +int +main () +{ + int fail; + struct sigaction sa; + + fail = init_ttys (); + if (fail) + return fail; + + if (setsid ()) + error (0, errno, "setsid"); + + sa.sa_handler = sighup; + sa.sa_flags = 0; /* No SA_RESTART! */ + sigemptyset(&sa.sa_mask); + if (sigaction (SIGHUP, &sa, NULL)) + error (2, errno, "cannot set SIGHUP handler"); + sa.sa_handler = sigterm; + if (sigaction (SIGTERM, &sa, NULL)) + error (2, errno, "cannot set SIGTERM handler"); + +#ifdef SIGLOST + /* We may generate SIGLOST signal from trying to talk to the console + after our port has been revoked or the term server has died. In that + case, reopen the console and restart. (Unfortunately this won't + restart the offending RPC on the new console port.) */ + if (signal (SIGLOST, reopen_console) == SIG_ERR) + error (2, errno, "cannot set SIGLOST handler"); +#endif + + /* Start up tty lines. */ + startup_ttys (); + + /* We will spend the rest of our life waiting for children to die. */ + while (1) + { + error_t waiterr; + pid_t pid = waitpid (WAIT_ANY, NULL, WUNTRACED); + waiterr = errno; + + /* Elicit a SIGLOST now if the console (on our stderr, i.e. fd 2) has + died. That way, the next error message emitted will actually make + it out to the console if it can be made it work at all. */ + write (2, "", 0); + + /* If a SIGTERM or SIGHUP arrived recently, it set a flag + and broke us out of being blocked in waitpid. */ + + if (pending_term) + { + pending_term = 0; + error (3, 0, "Got SIGTERM"); + } + if (pending_hup) + { + pending_hup = 0; + reread_ttys (); + } + + if (pid < 0) + { + if (waiterr == EINTR) /* A signal woke us. */ + continue; + error (1, waiterr, "waitpid"); + } + + assert (pid > 0); + + /* We have reaped a dead child. Restart that tty line. */ + restart_terminal (pid); + } +} |