From 5551fcc6442401d72b4db5ac5f5de471f07eeb0a Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Wed, 18 Sep 2013 15:29:36 +0200 Subject: [PATCH 4/8] init: add a minimalist init program This patch adds a minimalist init program. It is somewhat lacking in features, but is able to bring up a Hurd system with the runsystem and rc scripts. In fact, it roughly does what the former /hurd/init did, modulo all the very early bootstrapping stuff and the startup protocol. It is started when all the essential servers are up and running, so it can make use of most of the POSIX goodies, making its implementation much simpler. * Makefile (prog-subdirs): Add init. * daemons/runsystem.sh: Generalize runsystem so that it can start any init as specified on the kernel command line. By default, it starts /hurd/init. * daemons/runsystem.hurd: This is a verbatim copy of runsystem.sh. It is started by /hurd/init. * daemons/rc.sh: Do not start /hurd/mach-defpager as it is already started in runsystem.sh. * daemons/Makefile (SRCS): Add runsystem.hurd. * init/Makefile: New file. * init/init.c: Likewise. --- Makefile | 1 + daemons/Makefile | 4 +- daemons/rc.sh | 3 - daemons/runsystem.hurd | 155 +++++++++++++++++++++++++++++++++++++++++++++++++ daemons/runsystem.sh | 80 +++++++++---------------- init/Makefile | 24 ++++++++ init/init.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 364 insertions(+), 58 deletions(-) create mode 100644 daemons/runsystem.hurd create mode 100644 init/Makefile create mode 100644 init/init.c diff --git a/Makefile b/Makefile index 455df67..3178740 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,7 @@ prog-subdirs = auth proc exec term \ random \ procfs \ startup \ + init \ ifeq ($(HAVE_SUN_RPC),yes) prog-subdirs += nfs nfsd diff --git a/daemons/Makefile b/daemons/Makefile index d16680e..db1acc7 100644 --- a/daemons/Makefile +++ b/daemons/Makefile @@ -22,7 +22,9 @@ makemode := utilities 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 +SRCS = rc.sh runsystem.sh getty.c lmail.c console-run.c runttys.c \ + runsystem.hurd \ + installationdir = $(libexecdir) HURDLIBS = fshelp ports shouldbeinlibc diff --git a/daemons/rc.sh b/daemons/rc.sh index 5cf44fa..1240883 100644 --- a/daemons/rc.sh +++ b/daemons/rc.sh @@ -2,9 +2,6 @@ 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 diff --git a/daemons/runsystem.hurd b/daemons/runsystem.hurd new file mode 100644 index 0000000..f4f2771 --- /dev/null +++ b/daemons/runsystem.hurd @@ -0,0 +1,155 @@ +#!/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 +} + + +# See whether pflocal is set up already, and do so if not (install case) +# +# Normally this should be the case, but we better make sure since +# without the pflocal server, pipe(2) does not work. +if ! test -e /servers/socket/1 ; then + # The root filesystem should be read-only at this point. + if fsysopts / --update --writable ; then + settrans -c /servers/socket/1 /hurd/pflocal + else + singleuser "Failed to create /servers/socket/1." + fi +fi + +# 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/runsystem.sh b/daemons/runsystem.sh index f4f2771..bd145ed 100644 --- a/daemons/runsystem.sh +++ b/daemons/runsystem.sh @@ -1,9 +1,9 @@ #!/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. +# servers are up. It does some initialization of its own and then +# execs /hurd/init or any other roughly SysV init-compatible program +# to bring up the "userland" parts of a normal system. # @@ -22,11 +22,10 @@ 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' +# The init program to call. +# +# Can be overridden using init=something in the kernel command line. +init=/hurd/init ### @@ -44,7 +43,7 @@ trap 'reopen_console' SIGLOST # 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 () +function singleuser() { test $# -eq 0 || echo "$0: $*" for try in ${fallback_shells}; do @@ -54,6 +53,8 @@ function singleuser () exit 127 } +# Print a newline. +echo # See whether pflocal is set up already, and do so if not (install case) # @@ -85,20 +86,23 @@ fi # The first argument is the kernel file name; skip that. shift flags= +single= while [ $# -gt 0 ]; do arg="$1" shift case "$arg" in --*) ;; + init=*) + eval "${arg}" + ;; *=*) ;; -*) flags="${flags}${arg#-}" ;; - 'single'|'emergency') # Linux compat - flags="${flags}s" + 'single') + single="-s" ;; - 'fastboot') - flags="${flags}f" + 'fastboot'|'emergency') ;; esac done @@ -106,50 +110,18 @@ done # Check boot flags. case "$flags" in *s*) - rc=false # force single-user - ;; -*f*) - rc="${RUNCOM}" # fastboot - ;; -*) - rc="${RUNCOM} autoboot" # multi-user default + single="-s" # force single-user ;; 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 +# Start the default pager. It will bail if there is already one running. +/hurd/mach-defpager - # This program reads /etc/ttys and starts the programs it says to. - ${RUNTTYS} & - runttys_pid=$! +# This is necessary to make stat / return the correct device ids. +# Work around a race condition (probably in the root translator). +for i in `seq 1 100000` ; do : ; done - # Wait for runttys to die, meanwhile handling trapped signals. - wait +fsysopts / --update --readonly - # Go back to the top of the infinite loop, as if booting single-user. - rc=false - -done +# Finally, start the actual init. +exec ${init} ${single} -a diff --git a/init/Makefile b/init/Makefile new file mode 100644 index 0000000..07b8026 --- /dev/null +++ b/init/Makefile @@ -0,0 +1,24 @@ +# +# Copyright (C) 2013 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, see . + +dir := init +makemode := server + +SRCS = init.c +OBJS = $(SRCS:.c=.o) +target = init + +include ../Makeconf diff --git a/init/init.c b/init/init.c new file mode 100644 index 0000000..b3d3301 --- /dev/null +++ b/init/init.c @@ -0,0 +1,155 @@ +/* A minimalist init for the Hurd + + Copyright (C) 2013,14 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 Hurd. If not, see . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *argp_program_version = STANDARD_HURD_VERSION (init); +static pid_t child_pid; +static int single; + +static struct argp_option +options[] = +{ + /* XXX: Currently, -s does nothing. */ + {"single-user", 's', NULL, 0, "Startup system in single-user mode", 0}, + {NULL, 'a', NULL, 0, "Ignored for compatibility with sysvinit", 0}, + {0} +}; + +static char doc[] = "A minimalist init for the Hurd"; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 's': + single = 1; + break; + + case 'a': + /* Ignored. */ + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +void +sigchld_handler(int signal) +{ + /* A child died. Find its status. */ + int status; + pid_t pid; + + while (1) + { + pid = waitpid (WAIT_ANY, &status, WNOHANG | WUNTRACED); + if (pid <= 0) + break; /* No more children. */ + + /* Since we are init, orphaned processes get reparented to us and + alas, all our adopted children eventually die. Woe is us. We + just need to reap the zombies to relieve the proc server of + its burden, and then we can forget about the little varmints. */ + + if (pid == child_pid) + { + /* The big magilla bit the dust. */ + child_pid = -1; + + char *desc = NULL; + if (WIFSIGNALED (status)) + asprintf (&desc, "terminated abnormally (%s)", + strsignal (WTERMSIG (status))); + else if (WIFSTOPPED (status)) + asprintf (&desc, "stopped abnormally (%s)", + strsignal (WTERMSIG (status))); + else if (WEXITSTATUS (status) == 0) + desc = strdup ("finished"); + else + asprintf (&desc, "exited with status %d", + WEXITSTATUS (status)); + + error (0, 0, "child %s", desc); + free (desc); + + /* XXX: launch emergency shell. */ + error (23, 0, "panic!!"); + } + } +} + +int +main (int argc, char **argv) +{ + struct argp argp = + { + .options = options, + .parser = parse_opt, + .doc = doc, + }; + argp_parse (&argp, argc, argv, 0, 0, 0); + + if (getpid () != 1) + error (1, 0, "can only be run as PID 1"); + + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + + sigaction (SIGHUP, &sa, NULL); + sigaction (SIGINT, &sa, NULL); + sigaction (SIGQUIT, &sa, NULL); + sigaction (SIGTERM, &sa, NULL); + sigaction (SIGUSR1, &sa, NULL); + sigaction (SIGUSR2, &sa, NULL); + sigaction (SIGTSTP, &sa, NULL); + + sa.sa_handler = sigchld_handler; + sa.sa_flags |= SA_RESTART; + sigaction (SIGCHLD, &sa, NULL); + + char *args[] = { "/etc/hurd/runsystem.hurd", NULL }; + + switch (child_pid = fork ()) + { + case -1: + error (1, errno, "failed to fork"); + case 0: + execv (args[0], args); + error (2, errno, "failed to execv child"); + } + + select (0, NULL, NULL, NULL, NULL); + /* Not reached. */ + return 0; +} -- 2.1.0