diff options
Diffstat (limited to 'daemons')
-rw-r--r-- | daemons/ChangeLog | 197 | ||||
-rw-r--r-- | daemons/Makefile | 44 | ||||
-rw-r--r-- | daemons/console-run.c | 219 | ||||
-rw-r--r-- | daemons/getty.c | 115 | ||||
-rw-r--r-- | daemons/lmail.c | 542 | ||||
-rw-r--r-- | daemons/rc.sh | 111 | ||||
-rw-r--r-- | daemons/runsystem.sh | 146 | ||||
-rw-r--r-- | daemons/runttys.c | 471 |
8 files changed, 1845 insertions, 0 deletions
diff --git a/daemons/ChangeLog b/daemons/ChangeLog new file mode 100644 index 00000000..f93ca603 --- /dev/null +++ b/daemons/ChangeLog @@ -0,0 +1,197 @@ +1999-09-23 Mark Kettenis <kettenis@gnu.org> + + * getty.c (main): Report an error if login_tty failed. + +1999-09-22 Roland McGrath <roland@baalperazim.frob.com> + + * getty.c: Undo last change. + + * getty.c (main): Make TTY our controlling terminal with TIOCSCTTY. + +1999-09-19 Roland McGrath <roland@baalperazim.frob.com> + + * runttys.c (main): Only call error if setsid returns -1. + From Marcus Brinkmann <Marcus.Brinkmann@ruhr-uni-bochum.de>. + +1999-07-20 Roland McGrath <roland@baalperazim.frob.com> + + * getty.c: Include <utmp.h> for login_tty decl. + +1999-09-18 Marcus Brinkmann <Marcus.Brinkmann@ruhr-uni-bochum.de> + + * runttys.c (run): Only call error if setsid returns -1. + * console-run.c (open_console): Likewise. + +1999-07-10 Roland McGrath <roland@baalperazim.frob.com> + + * console-run.c (open_console): Move first setting of TERMINAL and + ARGZ_LEN outside of loop, quiets compiler warning. + + * lmail.c: Add #include <sys/mman.h> for munmap decl. + +1999-07-03 Thomas Bushnell, BSG <tb@mit.edu> + + * lmail.c (bfree): Use munmap instead of vm_deallocate. + +1999-07-01 Thomas Bushnell, BSG <tb@mit.edu> + + * rc.sh: Specify bash instead of sh. + (remove_translators): Don't descend into "." or "..". Alter + calling convention slightly and require the `nullglob' option to + be set. Change caller to suit. From Kalle Olavi Niemitalo + <tosi@ees2.oulu.fi>. + +1999-06-22 Roland McGrath <roland@baalperazim.frob.com> + + * rc.sh: Clean /tmp using special hackery to nuke translators. + +1999-06-18 Roland McGrath <roland@baalperazim.frob.com> + + * runsystem.sh: New file. + * Makefile (targets, special-targets): Add runsystem. + (runsystem): New target. + +1999-06-15 Roland McGrath <roland@baalperazim.frob.com> + + * runttys.c: New file.c + * Makefile (targets, SRCS): Add runttys, runttys.c. + (runttys): New target. + (runttys-LDLIBS): New variable, -lutil. + + * console-run.c: New file. + * Makefile (targets, SRCS): Add console-run, console-run.c. + (console-run): New target. + +1999-05-29 Roland McGrath <roland@baalperazim.frob.com> + + * rc.sh: Don't create /var/run/uptime. It's no longer used. + +1999-05-15 Roland McGrath <roland@baalperazim.frob.com> + + * rc.sh: Do `swapon -a' first thing. + +Tue Mar 9 13:18:14 1999 Thomas Bushnell, BSG <tb@mit.edu> + + * lmail.c (OPT_REMOVE): New macro. + (options): Don't recognize -r as an alias for --remove, but + recognize it separately as a hidden alias. + (main / parse_opt): Make -d do nothing. + Make -r an alias for -f. Have OPT_REMOVE do the removal thing. + Reported by Mark Kettenis (kettenis@wins.uva.nl). + +1998-12-06 Roland McGrath <roland@baalperazim.frob.com> + + * lmail.c (deliver, main): Add braces to silence gcc warning. + +1998-09-04 Roland McGrath <roland@baalperazim.frob.com> + + * lmail.c: Include <time.h> and <sys/time.h>. + +1998-07-19 Roland McGrath <roland@baalperazim.frob.com> + + * getty.c: Include <string.h> for basename decl. + +Wed Feb 19 23:05:13 1997 Miles Bader <miles@gnu.ai.mit.edu> + + * lmail.c (argp_program_version): Make const. + +Tue Oct 22 15:47:13 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * lmail.c (options): Make -l/--use-lock-file hidden, as we don't + support it anyway (we just leave it here in case someone does + implement it, so they'll know the right option to use for + compatibility). + +Thu Oct 17 11:06:44 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * lmail.c (deliver): fsync the user's mailbox after delivery. + +Mon Oct 14 21:36:29 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * lmail.c: New file. + * Makefile (targets): Add mail.local. + (SRCS): Add lmail.c. + (rc, mail.local): New rules. + (%: %.sh): Rule removed. + (INSTALL-mail.local-ops): New variable. + +Thu Oct 10 16:12:55 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * rc.sh: Startup up sendmail if possible. + +Thu Sep 12 16:50:12 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> + + * Makefile (HURDLIBS): New variable. + +Wed Aug 14 10:22:39 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * Makefile (getty-LDLIBS): New variable. + (getty): Dependency on -lutil removed. + (libutil-libsubst): Variable removed. + +Tue Aug 13 08:13:04 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> + + * rc.sh: When deleting files from /tmp, use -f flag to rm. + +Sat Jul 20 01:06:07 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * getty.c (main): Get the tty name from the right element in ARGV. + +Mon Jul 15 17:27:27 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * rc.sh: Create /var/run/uptime *after* clearing /var/run. Always + create /var/run/mtab to keep e2fsck happy. + +Fri Jul 12 16:49:33 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * rc.sh: Only clean /tmp and /var/run if they are respectively + directories. + +Sun Jul 7 10:39:27 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * getty.c (print_banner): Use basename instead of rolling our own. + +Sat Jul 6 13:58:40 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * getty.c (print_banner): Don't include directory in terminal + name. + +Sat Jul 6 00:10:35 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * getty.c (print_banner): Use localhost instead of grotty loop. + Make static. + (main): Use syslog instead of error to report exec failure. + * Makefile (getty): Depend on ../libshouldbeinlibc/libshouldbeinlibc.a. + +Fri Jul 5 22:02:16 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * getty.c: Include <sys/utsname.h> and <stdlib.h>. + (print_banner): New function. + (main): Call print_banner. + + * getty.c (main): Return something. + +Tue Jul 2 23:36:33 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * rc.sh: Touch /var/run/uptime file. + +Mon Jul 1 17:54:39 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * getty.c (main): Don't time out hardwired lines. + Print an error if exec fails. + +Thu Jun 27 16:44:52 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * getty.c (main): Repair loop. + +Tue Jun 25 18:01:17 1996 Miles Bader <miles@gnu.ai.mit.edu> + + * rc.sh: Use real fsck instead of kluge. + + * Makefile (%: %.sh): Make target writable. + +Thu Jun 20 14:38:58 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> + + * rc.sh: Copied from ../config. + * getty.c: Copied from ../utils. + * Makefile: New file. diff --git a/daemons/Makefile b/daemons/Makefile new file mode 100644 index 00000000..72394733 --- /dev/null +++ b/daemons/Makefile @@ -0,0 +1,44 @@ +# Makefile for daemons +# +# Copyright (C) 1996,99 Free Software Foundation, Inc. +# +# This program 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. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +dir := daemons +makemode := utilities + +targets = rc getty mail.local console-run runttys runsystem +special-targets = rc runsystem +SRCS = rc.sh getty.c lmail.c console-run.c runttys.c +installationdir = $(libexecdir) + +HURDLIBS=shouldbeinlibc +OBJS = $(SRCS:.c=.o) +getty-LDLIBS = -lutil + +INSTALL-mail.local-ops = -o root -m 4755 + +include ../Makeconf + +rc: rc.sh +getty: getty.o ../libshouldbeinlibc/libshouldbeinlibc.a +mail.local: lmail.o ../libshouldbeinlibc/libshouldbeinlibc.a +console-run: console-run.o ../libfshelp/libfshelp.a ../libports/libports.a + +runttys: runttys.o +runttys-LDLIBS = -lutil + +runsystem: runsystem.sh diff --git a/daemons/console-run.c b/daemons/console-run.c new file mode 100644 index 00000000..ccfd3874 --- /dev/null +++ b/daemons/console-run.c @@ -0,0 +1,219 @@ +/* Run a program on the console, trying hard to get the console open. + Copyright (C) 1999 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <error.h> +#include <paths.h> +#include <hurd.h> +#include <hurd/fshelp.h> +#include <device/device.h> + +static mach_port_t +get_console () +{ + mach_port_t device_master, console; + error_t err = get_privileged_ports (0, &device_master); + + if (err) + return MACH_PORT_NULL; + + err = device_open (device_master, D_WRITE | D_READ, "console", &console); + if (err) + return MACH_PORT_NULL; + + return console; +} + +static int open_console (char **namep); + +int +main (int argc, char **argv) +{ + mach_port_t consdev = get_console (); + char *consname; + + if (consdev == MACH_PORT_NULL) + _exit (127); + + stdin = stdout = NULL; + stderr = mach_open_devstream (consdev, "w"); + if (!stderr) + _exit (127); + + if (argc < 2) + error (1, 0, "Usage: %s PROGRAM [ARG...]", program_invocation_short_name); + + if (open_console (&consname)) + setenv ("FALLBACK_CONSOLE", consname, 1); + + execv (argv[1], &argv[1]); + error (5, errno, "cannot execute %s", argv[1]); + /* NOTREACHED */ + return 127; +} + +/* Open /dev/console. If it isn't there, or it isn't a terminal, then + create /tmp/console and put the terminal on it. If we get EROFS, + in trying to create /tmp/console then as a last resort, put the + console on /tmp itself. If all fail, we exit. + + Return nonzero if the vanilla open of /dev/console didn't work. + In any case, after the console has been opened, put it on fds 0, 1, 2. */ +static int +open_console (char **namep) +{ +#define TERMINAL_FIRST_TRY "/hurd/term\0/tmp/console\0device\0console" +#define TERMINAL_SECOND_TRY "/hurd/term\0/tmp\0device\0console" + mach_port_t term, proc; + static char *termname; + struct stat st; + error_t err = 0; + int fd; + int fallback; + + termname = _PATH_CONSOLE; + term = file_name_lookup (termname, O_RDWR, 0); + if (term != MACH_PORT_NULL) + err = io_stat (term, &st); + else + err = errno; + if (err) + error (0, err, "%s", termname); + else if (st.st_fstype != FSTYPE_TERM) + error (0, 0, "%s: Not a terminal", termname); + + fallback = (term == MACH_PORT_NULL || err || st.st_fstype != FSTYPE_TERM); + if (fallback) + /* Start the terminal server ourselves. */ + { + size_t argz_len; /* Length of args passed to translator. */ + char *terminal; /* Name of term translator. */ + mach_port_t control; /* Control port for term translator. */ + int try; + + error_t open_node (int flags, + mach_port_t *underlying, + mach_msg_type_name_t *underlying_type) + { + term = file_name_lookup (termname, flags | O_CREAT|O_NOTRANS, 0666); + if (term == MACH_PORT_NULL) + { + error (0, errno, "%s", termname); + return errno; + } + + *underlying = term; + *underlying_type = MACH_MSG_TYPE_COPY_SEND; + + return 0; + } + + terminal = TERMINAL_FIRST_TRY; + argz_len = sizeof TERMINAL_FIRST_TRY; + for (try = 1; try < 3; ++try) + { + if (try == 2) + { + terminal = TERMINAL_SECOND_TRY; + argz_len = sizeof TERMINAL_SECOND_TRY; + } + + termname = terminal + strlen (terminal) + 1; /* first arg is name */ + + /* The callback to start_translator opens TERM as a side effect. */ + errno = + fshelp_start_translator (open_node, terminal, terminal, argz_len, + 3000, &control); + if (errno) + { + error (0, errno, "%s", terminal); + continue; + } + + errno = file_set_translator (term, 0, FS_TRANS_SET, 0, 0, 0, + control, MACH_MSG_TYPE_COPY_SEND); + mach_port_deallocate (mach_task_self (), control); + if (errno) + { + error (0, errno, "%s", termname); + continue; + } + mach_port_deallocate (mach_task_self (), term); + + /* Now repeat the open. */ + term = file_name_lookup (termname, O_RDWR, 0); + if (term == MACH_PORT_NULL) + { + error (0, errno, "%s", termname); + continue; + } + errno = io_stat (term, &st); + if (errno) + { + error (0, errno, "%s", termname); + term = MACH_PORT_NULL; + continue; + } + if (st.st_fstype != FSTYPE_TERM) + { + error (0, 0, "%s: Not a terminal", termname); + term = MACH_PORT_NULL; + continue; + } + + if (term != MACH_PORT_NULL) + { + error (0, 0, "Using temporary console %s", termname); + break; + } + } + + if (term == MACH_PORT_NULL) + error (2, 0, "Cannot start console terminal"); + } + + fd = openport (term, O_RDWR); + if (fd < 0) + error (3, errno, "Cannot open console"); + + if (fd != 0) + { + dup2 (fd, 0); + close (fd); + } + dup2 (0, 1); + dup2 (0, 2); + + if (setsid () == -1) + error (0, errno, "setsid"); + + /* Set the console to our pgrp. */ + tcsetpgrp (0, getpid ()); + + /* Put us in a new login collection. */ + proc = getproc (); + proc_make_login_coll (proc); + mach_port_deallocate (mach_task_self (), proc); + + *namep = termname; + return fallback; +} diff --git a/daemons/getty.c b/daemons/getty.c new file mode 100644 index 00000000..4810e6dd --- /dev/null +++ b/daemons/getty.c @@ -0,0 +1,115 @@ +/* Stubby version of getty for Hurd + Copyright (C) 1996, 1998, 1999 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <syslog.h> +#include <unistd.h> +#include <ttyent.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <error.h> +#include <sys/utsname.h> +#include <stdlib.h> +#include <string.h> +#include <utmp.h> +#include <sys/ioctl.h> + +/* XXX */ +extern char *localhost (); + +#define _PATH_LOGIN "/bin/login" + +/* Print a suitable welcome banner */ +static void +print_banner (int fd, char *ttyname) +{ + int cc; + char *s; + struct utsname u; + char *hostname = localhost (); + + if (uname (&u)) + u.sysname[0] = u.release[0] = '\0'; + + cc = asprintf (&s, "\r\n\n%s %s (%s) (%s)\r\n\n", + u.sysname, u.release, hostname ?: "?", basename (ttyname)); + write (fd, s, cc); +} + +int +main (int argc, char **argv) +{ + char *linespec, *ttyname; + int tty; + struct ttyent *tt; + char *arg; + + openlog ("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH); + + /* Nothing to do .... */ + if (argc != 3) + { + syslog (LOG_ERR, "Bad syntax"); + closelog (); + exit (1); + } + + /* Don't do anything with this for now. */ + linespec = argv[2]; + + tt = getttynam (argv[2]); + asprintf (&ttyname, "%s/%s", _PATH_DEV, argv[2]); + + chown (ttyname, 0, 0); + chmod (ttyname, 0600); + revoke (ttyname); + sleep (2); /* leave DTR down for a bit */ + + do + { + tty = open (ttyname, O_RDWR); + if (tty == -1) + { + syslog (LOG_ERR, "%s: %m", ttyname); + closelog (); + sleep (60); + } + } + while (tty == -1); + + print_banner (tty, ttyname); + + if (login_tty (tty) == -1) + syslog (LOG_ERR, "cannot set controlling terminal to %s: %m", ttyname); + + asprintf (&arg, "TERM=%s", tt ? tt->ty_type : "unknown"); + + if (tt && strcmp (tt->ty_type, "dialup") == 0) + /* Dialup lines time out (which is login's default). */ + execl (_PATH_LOGIN, "login", "-e", arg, 0); + else + /* Hardwired lines don't. */ + execl (_PATH_LOGIN, "login", "-e", arg, "-aNOAUTH_TIMEOUT", 0); + + syslog (LOG_ERR, "%s: %m", _PATH_LOGIN); + + return 1; +} diff --git a/daemons/lmail.c b/daemons/lmail.c new file mode 100644 index 00000000..ef616862 --- /dev/null +++ b/daemons/lmail.c @@ -0,0 +1,542 @@ +/* Local mail delivery + + Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <pwd.h> +#include <syslog.h> +#include <sysexits.h> +#include <paths.h> +#include <argp.h> +#include <hurd.h> +#include <hurd/fd.h> +#include <version.h> +#include <time.h> +#include <sys/time.h> +#include <sys/mman.h> + +#define OPT_FILE -5 +#define OPT_REMOVE -6 + +const char *argp_program_version = STANDARD_HURD_VERSION (mail.local); + +static const struct argp_option +options[] = +{ + {"from", 'f', "USER", 0, "Record sender as USER"}, + {0, 'd', 0, OPTION_ALIAS|OPTION_HIDDEN}, + {0, 'r', 0, OPTION_ALIAS|OPTION_HIDDEN}, + {"file", OPT_FILE, "FILE", 0, "Deliver FILE instead of standard input"}, + {"remove", OPT_REMOVE, 0, 0, "Remove FILE after successful delivery"}, + {"mail-dir",'m', "DIR", 0, "Look for mailboxes in DIR"}, + {"use-lock-file",'l', 0, OPTION_HIDDEN, + "Use a lock file instead of flock for mailboxes"}, + {0} +}; +static const char args_doc[] = "USER..."; +static const char doc[] = "Deliver mail to the local mailboxes of USER..."; + +#define HDR_PFX "From " /* Header, at the beginning of a line, + starting each msg in a mailbox. */ +#define ESC_PFX ">" /* Used to escape occurances of HDR_PFX in + the msg body. */ + +#define BMAX (64*1024) /* Chunk size for I/O. */ + +struct params +{ + char *from; /* Who the mail is from. */ + char *mail_dir; /* Mailbox directory. */ +}; + +/* Convert the system error code ERR to an appropiate exit value. This + function currently only returns three sorts: success, temporary failure, + or error, with exit codes 0, EX_TEMPFAIL, or EX_UNAVAILABLE. The table of + temporary failures is from the bsd original of this program. */ +static int +err_to_ex (error_t err) +{ + switch (err) + { + case 0: + return 0; /* Success */ + /* Temporary failures: */ +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: /* Operation would block. */ +#endif + case EAGAIN: /* Resource temporarily unavailable */ + case EDQUOT: /* Disc quota exceeded */ + case EBUSY: /* Device busy */ + case EPROCLIM: /* Too many processes */ + case EUSERS: /* Too many users */ + case ECONNABORTED: /* Software caused connection abort */ + case ECONNREFUSED: /* Connection refused */ + case ECONNRESET: /* Connection reset by peer */ + case EDEADLK: /* Resource deadlock avoided */ + case EFBIG: /* File too large */ + case EHOSTDOWN: /* Host is down */ + case EHOSTUNREACH: /* No route to host */ + case EMFILE: /* Too many open files */ + case ENETDOWN: /* Network is down */ + case ENETRESET: /* Network dropped connection on reset */ + case ENETUNREACH: /* Network is unreachable */ + case ENFILE: /* Too many open files in system */ + case ENOBUFS: /* No buffer space available */ + case ENOMEM: /* Cannot allocate memory */ + case ENOSPC: /* No space left on device */ + case EROFS: /* Read-only file system */ + case ESTALE: /* Stale NFS file handle */ + case ETIMEDOUT: /* Connection timed out */ + return EX_TEMPFAIL; + default: + return EX_UNAVAILABLE; + } +} + +/* Print and syslog the given error message, with the system error string for + ERRNO appended. Return an appropiate exit code for ERRNO. */ +#define SYSERR(fmt, args...) \ + ({ syslog (LOG_ERR, fmt ": %m" , ##args); err_to_ex (errno); }) + +/* Print and syslog the given error message. Return the exit code + EX_UNAVAILABLE. */ +#define ERR(fmt, args...) \ + ({ syslog (LOG_ERR, fmt , ##args); EX_UNAVAILABLE; }) + +/* Print and syslog the given error message, with the system error string for + CODE appended. Return an appropiate exit code for CODE. */ +#define SYSERRX(code, fmt, args...) \ + ({ error_t _code = (code); \ + syslog (LOG_ERR, fmt ": %s" , ##args , strerror (_code)); \ + err_to_ex (_code); }) + +/* Block I/O functions. These are structured to allow the use of a read + method that avoids copying the data locally. */ + +/* Free block allocated by bread. */ +static void +bfree (char *blk, size_t blk_len) +{ + if (blk_len > 0) + munmap (blk, blk_len); +} + +/* Read up to MAX chars from IN into BLK & BLK_LEN, which may be reused or + freed. */ +static int +bread (int in, char *in_name, size_t max, char **blk, size_t *blk_len) +{ + char *orig_blk = *blk; + size_t orig_blk_len = *blk_len; + error_t err = HURD_DPORT_USE (in, io_read (port, blk, blk_len, -1, max)); + + if (err) + return SYSERRX (err, "%s", in_name); + + if (*blk != orig_blk) + bfree (orig_blk, orig_blk_len); + + return 0; +} + +/* Write BLK & BLK_LEN to OUT. An exit status is returned. */ +static int +bwrite (int out, char *out_name, const char *blk, size_t blk_len) +{ + while (blk_len > 0) + { + ssize_t wr = write (out, blk, blk_len); + if (wr < 0) + return SYSERR ("%s", out_name); + blk += wr; + blk_len -= wr; + } + return 0; +} + +/* Copy from file descriptor IN to OUT. An exit status is returned. */ +static int +copy (int in, char *in_name, int out, char *out_name) +{ + int ex = 0; + char *blk = 0; + size_t blk_len = 0; + + do + { + ex = bread (in, in_name, BMAX, &blk, &blk_len); + if (! ex) + ex = bwrite (out, out_name, blk, blk_len); + } + while (blk_len > 0 && !ex); + + bfree (blk, blk_len); + + return ex; +} + +static int +write_header (int out, char *out_name, struct params *params) +{ + char *hdr; + size_t hdr_len; + struct timeval tv; + time_t time; + int ex = 0; + + if (gettimeofday (&tv, 0) < 0) + return SYSERR ("gettimeofday"); + + /* Note that the string returned by ctime includes a terminating newline. */ + time = tv.tv_sec; + hdr_len = asprintf (&hdr, "From %s %s", params->from, ctime (&time)); + if (! hdr) + return SYSERRX (ENOMEM, "%s", out_name); + + ex = bwrite (out, out_name, hdr, hdr_len); + + free (hdr); + + return ex; +} + +/* Copy from file descriptor IN to OUT, making any changes needed to make the + contents a valid mailbox entry. These include: + (1) Prepending a `From ...' line, and appending a blank line. + (2) Replacing any occurances of `From ' at the beginning of lines with + `>From '. + An exit status is returned. */ +static int +process (int in, char *in_name, int out, char *out_name, struct params *params) +{ + /* The block currently being processed. */ + char *blk = 0; + size_t blk_len = 0; + /* MATCH is the string we're looking for to escape, NL_MATCH is the same + string prefixed by a newline to ease searching (MATCH only matches at + the beginning of lines). */ + const char *const nl_match = "\n" HDR_PFX, *const match = nl_match + 1; + /* The portion of MATCH that matched the end of the previous block. 0 + means that there was at least a newline, so initializing MATCHED to 0 + simulates a newline at the start of IN. */ + ssize_t matched = 0; + int ex = write_header (out, out_name, params); + +#define match_len (sizeof HDR_PFX - 1) + + if (ex) + return ex; + +#define BWRITE(p, p_len) \ + ({ size_t _len = (p_len); \ + if (_len > 0 && (ex = bwrite (out, out_name, p, _len))) \ + break; }) + + do + { + char *start, *end; + + ex = bread (in, in_name, BMAX, &blk, &blk_len); + + if (matched >= 0) + /* The last block ended in a partial match, so see if we can complete + it in this block. */ + { + if (blk_len >= match_len - matched + && memcmp (blk, match + matched, match_len - matched) == 0) + /* It matched; output the escape prefix before the match. */ + BWRITE (ESC_PFX, sizeof ESC_PFX - 1); + /* Now we have to output the part of the preceding block that we + didn't do before because it was a partial match. */ + BWRITE (match, matched); + /* Ok, we're all caught up. The portion of the match that's in + this block will be output as normal text. */ + matched = -1; + } + + /* Scan through the block looking for matches. */ + for (start = end = blk; start < blk + blk_len; start = end) + { + /* Look for our match, prefixed by a newline. */ + end = memmem (start, blk + blk_len - start, nl_match, match_len + 1); + if (end) + /* We found a match, output the escape prefix before it. */ + { + end++; /* The newline should precede the escape. */ + BWRITE (start, end - start); /* part of block before match. */ + BWRITE (ESC_PFX, sizeof ESC_PFX - 1); /* escape prefix. */ + } + else + { + end = blk + blk_len; + break; + } + } + + /* Now see if there are any partial matches at the end. */ + for (matched = + end - start < match_len + 1 ? end - start - 1 : match_len; + matched >= 0; + matched--) + if (memcmp (end - matched - 1, nl_match, matched + 1) == 0) + /* There's a partial match MATCHED characters long at the end of + this block, so delay outputting it until the next block can be + examined; we do output the preceding newline here, though. */ + { + end -= matched; + break; + } + + BWRITE (start, end - start); + } + while (blk_len > 0); + + if (! ex) + ex = bwrite (out, out_name, "\n", 1); /* Append a blank line. */ + + bfree (blk, blk_len); + + return ex; +} + +/* Deliver flags. */ +#define D_PROCESS 0x1 /* Deliver */ +#define D_REWIND 0x2 /* Rewind MSG before using it. */ + +/* Deliver the text from the file descriptor MSG to the mailbox of the user + RCPT in MAIL_DIR. FLAGS is from the set D_* above. An exit appropiate + exit code is returned. */ +static int +deliver (int msg, char *msg_name, char *rcpt, int flags, struct params *params) +{ + char *mbox; /* Name of mailbox */ + int fd; /* Opened mailbox */ + struct stat stat; + int ex = 0; /* Exit status */ + struct passwd *pw = getpwnam (rcpt); /* Details of recipient */ + + if (! pw) + return ERR ("%s: Unknown user", rcpt); + + asprintf (&mbox, "%s/%s", params->mail_dir, rcpt); + if (! mbox) + return SYSERRX (ENOMEM, "%s", rcpt); + + do + { + /* First try to open an existing mailbox. */ + fd = open (mbox, O_WRONLY|O_APPEND|O_NOLINK|O_EXLOCK); + if (fd < 0 && errno == ENOENT) + /* There is none; try to create it. */ + { + fd = open (mbox, O_WRONLY|O_APPEND|O_CREAT|O_EXCL|O_NOLINK|O_EXLOCK, + S_IRUSR|S_IWUSR); + if (fd >= 0) + /* Made a new mailbox! Set the owner and group appropiately. */ + { + if (fchown (fd, pw->pw_uid, pw->pw_gid) < 0) + { + close (fd); + fd = -1; /* Propagate error. */ + } + } + } + } + /* EEXIST can only be returned someone else created the mailbox between the + two opens above, so if we try again, the first open should work. */ + while (fd < 0 && errno == EEXIST); + + if (fd < 0 || fstat (fd, &stat) < 0) + ex = SYSERR ("%s", mbox); + else if (S_ISLNK (stat.st_mode) || stat.st_nlink != 1) + ex = ERR ("%s: Is linked", mbox); + else + { + if (flags & D_REWIND) + { + if (lseek (msg, 0L, SEEK_SET) < 0) + ex = SYSERR ("%s", msg_name); + } + if (! ex) + { + if (flags & D_PROCESS) + ex = process (msg, msg_name, fd, mbox, params); + else + ex = copy (msg, msg_name, fd, mbox); + } + } + + if (fd >= 0) + { + if (fsync (fd) < 0 && !ex) + ex = SYSERR ("%s", mbox); + if (close (fd) < 0 && !ex) + ex = SYSERR ("%s", mbox); + } + free (mbox); + + return ex; +} + +/* Process from the file descriptor IN into a temporary file, which return a + descriptor to in CACHED; once *CACHED is closed, it will go away + permanently. The file pointer of *CACHED is at an undefined location. An + exit status is returned. */ +static int +cache (int in, char *in_name, struct params *params, int *cached) +{ + int ex; + error_t err; + file_t file; /* Hurd port for temp file */ + int fd; /* File descriptor for it */ + file_t tmp_dir = file_name_lookup (_PATH_TMP, O_RDONLY, 0); + + if (tmp_dir == MACH_PORT_NULL) + return SYSERR ("%s", _PATH_TMP); + + /* Create FILE without actually putting it into TMP_DIR. */ + err = dir_mkfile (tmp_dir, O_RDWR, 0600, &file); + if (err) + return SYSERRX (err, "%s", _PATH_TMP); + + fd = _hurd_intern_fd (file, O_RDWR, 1); + if (fd < 0) + return SYSERR ("%s", _PATH_TMP); + + ex = process (in, in_name, fd, _PATH_TMP, params); + if (! ex) + *cached = fd; + else + close (fd); + + return ex; +} + +int +main (int argc, char **argv) +{ + int rcpt = 0; /* Index in ARGV of next recepient. */ + char *file = 0; /* File containing message. */ + int remove = 0; /* Remove file after successful delivery. */ + int in = 0; /* Raw input file descriptor. */ + int ex = 0; /* Exit status. */ + struct params params = { from: 0, mail_dir: _PATH_MAILDIR }; + + error_t parse_opt (int key, char *arg, struct argp_state *state) + { + switch (key) + { + case 'd': + /* do nothing; compatibility */ + break; + case 'f': + case 'r': + params.from = arg; break; + case OPT_FILE: + file = arg; break; + case OPT_REMOVE: + remove = 1; break; + case 'm': + params.mail_dir = arg; break; + case 'l': + argp_failure (state, EX_USAGE, EINVAL, "-l not supported"); + case ARGP_KEY_NO_ARGS: + argp_error (state, "No recipients"); + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + const struct argp argp = { options, parse_opt, args_doc, doc }; + + /* Parse arguments. */ + argp_parse (&argp, argc, argv, 0, &rcpt, 0); + + /* Use syslog to log errors. */ + openlog ("mail.local", LOG_PERROR, LOG_MAIL); + + if (! params.from) + /* No from address specified, use the current user. */ + { + struct passwd *pw; + int uid = getuid (); + + if (uid == -1) + exit (ERR ("No user id")); + + pw = getpwuid (uid); + if (! pw) + exit (ERR ("%d: Unknown uid", uid)); + + params.from = strdup (pw->pw_name); + } + + if (file) + /* Use FILE as the message contents. */ + { + in = open (file, O_RDONLY); + if (in < 0) + exit (SYSERR ("%s", file)); + } + else + /* Use standard input. */ + in = 0; + + if (rcpt == argc - 1) + /* Only a single recipient. */ + ex = deliver (in, file ?: "-", argv[rcpt], D_PROCESS, ¶ms); + else + /* Multiple recipients. */ + { + int cached; /* Temporary processed input file. */ + + ex = cache (in, file ?: "-", ¶ms, &cached); + if (! ex) + while (rcpt < argc) + { + /* Deliver to one recipient. */ + int rex = deliver (cached, "message cache", argv[rcpt++], + D_REWIND, ¶ms); + + /* Merge the exit code for that recipient. Temporary failures + take precedence over hard failures and success, as + subsequently delivering duplicates (of the successful + messages) is preferable to not delivering the temporary + failures. */ + if (ex != EX_TEMPFAIL) + { + if (rex == EX_TEMPFAIL) + ex = EX_TEMPFAIL; + else if (! ex) + ex = rex; + } + } + } + + if (file && remove && !ex) + unlink (file); + + exit (ex); +} diff --git a/daemons/rc.sh b/daemons/rc.sh new file mode 100644 index 00000000..75afa4df --- /dev/null +++ b/daemons/rc.sh @@ -0,0 +1,111 @@ +#!/bin/bash +PATH=/bin:/sbin + +swapon -a + +if [ -r /fastboot ] +then + rm -f /fastboot + echo Fast boot ... skipping disk checks +elif [ $1x = autobootx ] +then + echo Automatic boot in progress... + date + + /sbin/fsck --preen --writable + + case $? in + # Successful completion + 0) + ;; + # Filesystem modified (but ok now) + 1 | 2) + ;; + # Fsck couldn't fix it. + 4 | 8) + echo "Automatic boot failed... help!" + exit 1 + ;; + # Signal that really interrupted something + 20 | 130 | 131) + echo "Boot interrupted" + exit 1 + ;; + # Special `let fsck finish' interruption (SIGQUIT) + 12) + echo "Boot interrupted (filesystem checks complete)" + exit 1 + ;; + # Oh dear. + *) + echo "Unknown error during fsck" + exit 1 + ;; + esac +fi + +echo -n cleaning up left over files... +rm -f /etc/nologin +rm -f /var/lock/LCK.* +if test -d /tmp; then + + # Forcibly remove all translators in the directory. + # It is then safe to attempt to remove files and descend directories. + # All parameters must begin with "./". + function remove_translators() { + local f + for f; do + settrans -pagfS "$f" + if [ -L "$f" ] || [ ! -d "$f" ]; then + rm "$f" + else + remove_translators "$f"/* "$f"/.[!.] "$f"/.??* + rmdir "$f" + fi + done + } + + (cd /tmp + shopt -s nullglob + for f in * .[!.] .??*; do + case "$f" in + 'lost+found'|'quotas') ;; + *) remove_translators "./$f" + esac + done) + + unset -f remove_translators # because it relies on nullglob + +fi +if test -d /var/run; then + (cd /var/run && { rm -rf -- *; cp /dev/null utmp; chmod 644 utmp; }) +fi +echo done + +# This file must exist for e2fsck to work. XXX +touch /var/run/mtab + +#echo -n restoring pty permissions... +#chmod 666 /dev/tty[pqrs]* +#echo done + +#echo -n updating /etc/motd... +#echo GNU\'s Not Unix Version `uname --release` > /tmp/newmotd +#egrep -v 'GNU|Version' /etc/motd >> /tmp/newmotd +#mv /tmp/newmotd /etc/motd +#echo done + +chmod 664 /etc/motd + +echo -n starting daemons: + +/sbin/syslogd && echo -n ' syslogd' +/sbin/inetd && echo -n ' inetd' + +if test -x /sbin/sendmail -a -r /etc/sendmail.cf; then + /sbin/sendmail -bd -q30m && echo -n ' sendmail' +fi + +echo . + +date diff --git a/daemons/runsystem.sh b/daemons/runsystem.sh new file mode 100644 index 00000000..afedd5c9 --- /dev/null +++ b/daemons/runsystem.sh @@ -0,0 +1,146 @@ +#!/bin/sh +# +# This program is run by /hurd/init at boot time after the essential +# servers are up, and is responsible for running the "userland" parts of a +# normal system. This includes running the single-user shell as well as a +# multi-user system. This program is expected never to exit. +# + + +### +### Where to find programs, etc. +### + +PATH=/bin:/sbin +export PATH + +# If we lose badly, try to exec each of these in turn. +fallback_shells='/bin/sh /bin/bash /bin/csh /bin/ash /bin/shd' + +# Shell used for normal single-user startup. +SHELL=/bin/sh + +# Programs that do multi-user startup. +RUNCOM=/libexec/rc +RUNTTYS=/libexec/runttys +# Signals that we should pass down to runttys. +runttys_sigs='TERM INT HUP TSTP' + +### + + +# If we get a SIGLOST, attempt to reopen the console in case +# our console ports were revoked. This lets us print messages. +function reopen_console () +{ + exec 1>/dev/console 2>&1 || exit 3 +} +trap 'reopen_console' SIGLOST + + +# Call this when we are losing badly enough that we want to punt normal +# startup entirely. We exec a single-user shell, so we will not come back +# here. The only way to get to multi-user from that shell will be +# explicitly exec this script or something like that. +function singleuser () +{ + test $# -eq 0 || echo "$0: $*" + for try in ${fallback_shells}; do + SHELL=${try} + exec ${SHELL} + done + exit 127 +} + + +# We expect to be started by console-run, which gives us no arguments and +# puts FALLBACK_CONSOLE=file-name in the environment if our console is +# other than a normal /dev/console. The MULTIBOOT_CMDLINE environment +# variable gives us the kernel command line from the boot loader, which is +# where any interesting boot options will be found. + +if [ "${FALLBACK_CONSOLE+set}" = set ]; then + singleuser "Running on fallback console ${FALLBACK_CONSOLE}" +elif [ $# -ne 0 ]; then + singleuser "Unexpected arguments: $*" +elif [ "${MULTIBOOT_CMDLINE+set}" != set ]; then + singleuser "No multiboot command line!" +fi + + +### +### Normal startup procedures +### + +# Parse the multiboot command line. We only pay attention to -s and -f. +set -- ${MULTIBOOT_CMDLINE} +shift +flags= +while [ $# -gt 0 ]; do + arg="$1" + shift + case "$arg" in + --*) ;; + *=*) ;; + -*) + flags="${flags}${arg#-}" + ;; + 'single'|'emergency') # Linux compat + flags="${flags}s" + ;; + 'fastboot') + flags="${flags}f" + ;; + esac +done + +# Check boot flags. +case "$flags" in +*s*) + rc=false # force single-user + ;; +*f*) + rc="${RUNCOM}" # fastboot + ;; +*) + rc="${RUNCOM} autoboot" # multi-user default + ;; +esac + +# Large infinite loop. If this script ever exits, init considers that +# a serious bogosity and punts to a fallback single-user shell. +# We handle here the normal transitions between single-user and multi-user. +while : ; do + + # Run the rc script. As long as it exits nonzero, punt to single-user. + # After the single-user shell exits, we will start over attempting to + # run rc; but later invocations strip the `autoboot' argument. + until $rc; do + rc=${RUNCOM} + + # Run single-user shell and repeat as long as it dies with a signal. + until ${SHELL} || test $? -lt 128; do + : + done + done + + # Now we are officially ready for normal multi-user operation. + + # Trap certain signals and send them on to runttys. For this to work, we + # must run it asynchronously and wait for it with the `wait' built-in. + runttys_pid=0 + for sig in $runttys_sigs; do + trap "kill -$sig \${runttys_pid}" $sig + done + + # This program reads /etc/ttys and starts the programs it says to. + ${RUNTTYS} & + runttys_pid=$! + + # Wait for runttys to die, meanwhile handling trapped signals. + wait + + # Go back to the top of the infinite loop, as if booting single-user. + rc=false + +done diff --git a/daemons/runttys.c b/daemons/runttys.c new file mode 100644 index 00000000..091cf519 --- /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 () == -1) + 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 () == -1) + 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); + } +} |