aboutsummaryrefslogtreecommitdiff
path: root/daemons
diff options
context:
space:
mode:
Diffstat (limited to 'daemons')
-rw-r--r--daemons/ChangeLog197
-rw-r--r--daemons/Makefile44
-rw-r--r--daemons/console-run.c219
-rw-r--r--daemons/getty.c115
-rw-r--r--daemons/lmail.c542
-rw-r--r--daemons/rc.sh111
-rw-r--r--daemons/runsystem.sh146
-rw-r--r--daemons/runttys.c471
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, &params);
+ else
+ /* Multiple recipients. */
+ {
+ int cached; /* Temporary processed input file. */
+
+ ex = cache (in, file ?: "-", &params, &cached);
+ if (! ex)
+ while (rcpt < argc)
+ {
+ /* Deliver to one recipient. */
+ int rex = deliver (cached, "message cache", argv[rcpt++],
+ D_REWIND, &params);
+
+ /* 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);
+ }
+}