aboutsummaryrefslogtreecommitdiff
path: root/daemons
diff options
context:
space:
mode:
Diffstat (limited to 'daemons')
-rw-r--r--daemons/ChangeLog62
-rw-r--r--daemons/Makefile25
-rw-r--r--daemons/console-run.c220
-rw-r--r--daemons/getty.c49
-rw-r--r--daemons/lmail.c542
-rw-r--r--daemons/rc.sh60
-rw-r--r--daemons/runsystem.sh142
-rw-r--r--daemons/runttys.c502
8 files changed, 1508 insertions, 94 deletions
diff --git a/daemons/ChangeLog b/daemons/ChangeLog
deleted file mode 100644
index de2a49c3..00000000
--- a/daemons/ChangeLog
+++ /dev/null
@@ -1,62 +0,0 @@
-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
index b089ac79..d16680ec 100644
--- a/daemons/Makefile
+++ b/daemons/Makefile
@@ -1,6 +1,6 @@
# Makefile for daemons
-#
-# Copyright (C) 1996 Free Software Foundation
+#
+# Copyright (C) 1996, 1999, 2008, 2010 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
@@ -20,20 +20,25 @@
dir := daemons
makemode := utilities
-targets = rc getty
-special-targets = rc
-SRCS = rc.sh getty.c
+targets = rc getty mail.local console-run runttys runsystem
+special-targets = rc runsystem
+SRCS = rc.sh runsystem.sh getty.c lmail.c console-run.c runttys.c
installationdir = $(libexecdir)
+HURDLIBS = fshelp ports shouldbeinlibc
OBJS = $(SRCS:.c=.o)
+getty-LDLIBS = -lutil
-libutil-libsubst = -lutil
+INSTALL-mail.local-ops = -o root -m 4755
include ../Makeconf
-%: %.sh
- cp $< $@ && chmod +w $@
+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
-getty: ../libshouldbeinlibc/libshouldbeinlibc.a -lutil
+runttys: runttys.o
+runttys-LDLIBS = -lutil
-$(filter-out $(special-targets), $(targets)): %: %.o
+runsystem: runsystem.sh
diff --git a/daemons/console-run.c b/daemons/console-run.c
new file mode 100644
index 00000000..3ab2629a
--- /dev/null
+++ b/daemons/console-run.c
@@ -0,0 +1,220 @@
+/* Run a program on the console, trying hard to get the console open.
+ Copyright (C) 1999, 2001 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,
+ task_t task, void *cookie)
+ {
+ 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. */
+ err =
+ fshelp_start_translator (open_node, NULL, terminal, terminal,
+ argz_len, 3000, &control);
+ if (err)
+ {
+ error (0, err, "%s", terminal);
+ continue;
+ }
+
+ err = 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 (err)
+ {
+ error (0, err, "%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;
+ }
+ err = io_stat (term, &st);
+ if (err)
+ {
+ error (0, err, "%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
index 013c3e3a..5a2896d8 100644
--- a/daemons/getty.c
+++ b/daemons/getty.c
@@ -1,5 +1,7 @@
/* Stubby version of getty for Hurd
- Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Copyright (C) 1996, 1998, 1999, 2007 Free Software Foundation, Inc.
+
Written by Michael I. Bushnell, p/BSG.
This file is part of the GNU Hurd.
@@ -28,14 +30,36 @@
#include <error.h>
#include <sys/utsname.h>
#include <stdlib.h>
+#include <string.h>
+#include <utmp.h>
+#include <sys/ioctl.h>
+#include <termios.h>
/* XXX */
-extern int login_tty (int);
extern char *localhost ();
-#define _PATH_DEV "/dev"
#define _PATH_LOGIN "/bin/login"
+/* Parse the terminal speed. */
+static void
+set_speed (int tty, char *speedstr)
+{
+ error_t err;
+ struct termios ttystat;
+ speed_t speed;
+ char *tail;
+
+ errno = 0;
+ speed = strtoul (speedstr, &tail, 0);
+ if (errno || *tail)
+ return;
+
+ err = tcgetattr (tty, &ttystat);
+ if (!err && !cfsetspeed (&ttystat, speed))
+ tcsetattr (tty, TCSAFLUSH, &ttystat);
+}
+
+
/* Print a suitable welcome banner */
static void
print_banner (int fd, char *ttyname)
@@ -62,7 +86,7 @@ main (int argc, char **argv)
char *arg;
openlog ("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
-
+
/* Nothing to do .... */
if (argc != 3)
{
@@ -72,16 +96,16 @@ main (int argc, char **argv)
}
/* Don't do anything with this for now. */
- linespec = argv[2];
+ linespec = argv[1];
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);
@@ -94,18 +118,21 @@ main (int argc, char **argv)
}
while (tty == -1);
+ set_speed (tty, linespec);
+
print_banner (tty, ttyname);
- login_tty (tty);
-
+ 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);
+ execl (_PATH_LOGIN, "login", "-e", arg, NULL);
else
/* Hardwired lines don't. */
- execl (_PATH_LOGIN, "login", "-e", arg, "-aNOAUTH_TIMEOUT", 0);
+ execl (_PATH_LOGIN, "login", "-e", arg, "-aNOAUTH_TIMEOUT", NULL);
syslog (LOG_ERR, "%s: %m", _PATH_LOGIN);
diff --git a/daemons/lmail.c b/daemons/lmail.c
new file mode 100644
index 00000000..23c7b497
--- /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 occurrences 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 appropriate 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 appropriate 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 appropriate 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 occurrences 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 appropriate
+ 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 recipient. */
+ 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
index b113dac1..57780945 100644
--- a/daemons/rc.sh
+++ b/daemons/rc.sh
@@ -1,8 +1,17 @@
-#!/bin/sh
+#!/bin/bash
+
PATH=/bin:/sbin
+# Start the default pager. It will bail if there is already one running.
+/hurd/mach-defpager
+
+# Set up swap space. This will complain if no default pager is functioning.
+swapon -a
+
+# Check filesystems.
if [ -r /fastboot ]
then
+ # ... or don't.
rm -f /fastboot
echo Fast boot ... skipping disk checks
elif [ $1x = autobootx ]
@@ -19,7 +28,7 @@ then
# Filesystem modified (but ok now)
1 | 2)
;;
- # Fsck couldn't fix it.
+ # Fsck couldn't fix it.
4 | 8)
echo "Automatic boot failed... help!"
exit 1
@@ -35,8 +44,8 @@ then
exit 1
;;
# Oh dear.
- *)
- echo "Unknown error during fsck"
+ *)
+ echo "Unknown error during fsck (exit status $?)"
exit 1
;;
esac
@@ -46,16 +55,40 @@ echo -n cleaning up left over files...
rm -f /etc/nologin
rm -f /var/lock/LCK.*
if test -d /tmp; then
- (cd /tmp; find . ! -name . ! -name lost+found ! -name quotas -exec rm -r {} \; )
+
+ # 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 records when the system was booted.
-date > /var/run/uptime
-
# This file must exist for e2fsck to work. XXX
touch /var/run/mtab
@@ -72,9 +105,14 @@ touch /var/run/mtab
chmod 664 /etc/motd
echo -n starting daemons:
-/sbin/syslogd; echo -n ' syslogd'
-/sbin/inetd; echo -n ' inet'
-echo .
+/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..c3cb2d62
--- /dev/null
+++ b/daemons/runsystem.sh
@@ -0,0 +1,142 @@
+#!/bin/bash
+#
+# 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
+
+umask 022
+
+# 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.
+
+if [ "${FALLBACK_CONSOLE+set}" = set ]; then
+ singleuser "Running on fallback console ${FALLBACK_CONSOLE}"
+fi
+
+
+###
+### Normal startup procedures
+###
+
+# Parse the multiboot command line. We only pay attention to -s and -f.
+# The first argument is the kernel file name; skip that.
+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..7efb7b7a
--- /dev/null
+++ b/daemons/runttys.c
@@ -0,0 +1,502 @@
+/* /etc/ttys support for Hurd
+ Copyright (C) 1993,94,95,96,97,98,99,2001 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 + 1) * sizeof (char *));
+ if (argv == 0)
+ error (0, ENOMEM,
+ "cannot allocate argument vector for %s", t->name);
+ else
+ argz_extract (argz, len, argv);
+ return argv;
+ }
+
+ char *line;
+ asprintf (&line, "%s %s", tt->ty_getty, tt->ty_name);
+ if (line == 0)
+ {
+ error (0, ENOMEM,
+ "cannot allocate arguments for %s", t->name);
+ t->getty_argv = 0;
+ }
+ else
+ {
+ 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)
+ {
+ struct terminal *newttys = realloc (ttys,
+ (ttyslen * 2) * sizeof ttys[0]);
+ if (newttys == 0)
+ {
+ error (0, ENOMEM, "cannot expand terminals table past %d", ttyslen);
+ return 0;
+ }
+ else
+ {
+ ttys = newttys;
+ memset (&ttys[nttys], 0, ttyslen);
+ ttyslen *= 2;
+ }
+ }
+
+ t = &ttys[nttys];
+ t->name = strdup (tt->ty_name);
+ if (t->name == 0)
+ {
+ error (0, ENOMEM, "cannot allocate entry for %s", tt->ty_name);
+ return 0;
+ }
+
+ nttys++;
+ 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 ttys[0]);
+ if (ttys == 0)
+ error (2, ENOMEM, "cannot allocate table");
+
+ 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 everything 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 (t == 0)
+ continue;
+ 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);
+ }
+}