diff options
Diffstat (limited to 'init')
-rw-r--r-- | init/init.c | 531 |
1 files changed, 422 insertions, 109 deletions
diff --git a/init/init.c b/init/init.c index e6443942..8c4e5135 100644 --- a/init/init.c +++ b/init/init.c @@ -1,4 +1,4 @@ -/* Init that only bootstraps the hurd and runs sh. +/* Somewhat primitive version of init Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc. This file is part of the GNU Hurd. @@ -39,6 +39,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <sys/wait.h> #include <hurd/msg_server.h> #include <wire.h> +#include <paths.h> +#include <sys/wait.h> +#include <error.h> +#include <hurd/msg_reply.h> #include "startup_notify_U.h" #include "startup_reply_U.h" @@ -51,6 +55,18 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define BOOT(flags) ((flags & RB_HALT) ? "halt" : "reboot") +#define _PATH_RUNCOM "/etc/rc" +#define _PATH_LOGIN "/bin/login" + +/* Current state of the system. */ +enum +{ + INITIAL, + SINGLE, + RUNCOM, + MULTI, +} system_state; + /* This structure keeps track of each notified task. */ struct ntfy_task { @@ -80,6 +96,10 @@ mach_port_t host_priv, device_master; /* Args to bootstrap, expressed as flags */ int bootstrap_args; +/* Set if something determines we should no longer pass the `autoboot' + flag to _PATH_RUNCOM. */ +int do_fastboot; + /* Stored information for returning proc and auth startup messages. */ mach_port_t procreply, authreply; mach_msg_type_name_t procreplytype, authreplytype; @@ -101,10 +121,13 @@ char *init_version = "0.0"; mach_port_t default_ports[INIT_PORT_MAX]; mach_port_t default_dtable[3]; +int default_ints[INIT_INT_MAX]; char **global_argv; pid_t shell_pid; /* PID of single-user shell. */ +pid_t rc_pid; /* PID of rc script */ +pid_t session_pid; /* PID of sole multi-user login */ /* Read a string from stdin into BUF. */ static int @@ -126,17 +149,19 @@ reboot_mach (int flags) { if (fakeboot) { - printf ("init: Would %s Mach with flags %#x\n", BOOT (flags), flags); + printf ("%s: Would %s Mach with flags %#x\n", + program_invocation_short_name, BOOT (flags), flags); fflush (stdout); exit (1); } else { - printf ("init: %sing Mach (flags %#x)...\n", BOOT (flags), flags); + printf ("%s: %sing Mach (flags %#x)...\n", + program_invocation_short_name, BOOT (flags), flags); fflush (stdout); sleep (5); while ((errno = host_reboot (host_priv, flags))) - perror ("host_reboot"); + error (0, errno, "reboot"); for (;;); } } @@ -148,27 +173,34 @@ crash_mach (void) reboot_mach (CRASH_FLAGS); } -/* Reboot the Hurd. */ +/* Notify all tasks that have requested shutdown notifications */ void -reboot_system (int flags) +notify_shutdown (char *msg) { struct ntfy_task *n; for (n = ntfy_tasks; n != NULL; n = n->next) { error_t err; - printf ("%s: notifying %s of shutdown...", - program_invocation_short_name, n->name); + printf ("%s: notifying %s of %s...", + program_invocation_short_name, n->name, msg); fflush (stdout); err = startup_dosync (n->notify_port, 60000); /* 1 minute to reply */ if (err == MACH_SEND_INVALID_DEST) - printf ("(no longer present)\n"); + puts ("(no longer present)"); else if (err) - printf ("%s", strerror (err)); + puts (strerror (err)); else - printf ("done\n"); + puts ("done"); fflush (stdout); } +} + +/* Reboot the Hurd. */ +void +reboot_system (int flags) +{ + notify_shutdown ("shutdown"); if (fakeboot) { @@ -182,22 +214,19 @@ reboot_system (int flags) { procbad: /* The procserver must have died. Give up. */ - printf ("Init: can't simulate crash; proc has died\n"); - fflush (stdout); + error (0, 0, "Can't simulate crash; proc has died"); reboot_mach (flags); } for (ind = 0; ind < npids; ind++) { task_t task; + err = proc_pid2task (procserver, pp[ind], &task); if (err == MACH_SEND_INVALID_DEST) goto procbad; - else if (err) { - printf ("init: getting task for pid %d: %s\n", - pp[ind], strerror (err)); - fflush (stdout); + error (0, err, "Getting task for pid %d", pp[ind]); continue; } @@ -215,14 +244,13 @@ reboot_system (int flags) goto procbad; if (err) { - printf ("init: getting procinfo for pid %d: %s\n", - pp[ind], strerror (err)); - fflush (stdout); + error (0, err, "Getting procinfo for pid %d", pp[ind]); continue; } if (!(pi->state & PI_NOPARENT)) { - printf ("init: killing pid %d\n", pp[ind]); + printf ("%s: Killing pid %d\n", + program_invocation_short_name, pp[ind]); fflush (stdout); task_terminate (task); } @@ -231,10 +259,10 @@ reboot_system (int flags) (vm_address_t)noise, noise_len); } } - printf ("Killing proc server\n"); + printf ("%s: Killing proc server\n", program_invocation_short_name); fflush (stdout); task_terminate (proctask); - printf ("Init exiting\n"); + printf ("%s: Exiting", program_invocation_short_name); fflush (stdout); } reboot_mach (flags); @@ -268,7 +296,7 @@ run (char *server, mach_port_t *ports, task_t *task) file = file_name_lookup (prog, O_EXEC, 0); if (file == MACH_PORT_NULL) - perror (prog); + error (0, errno, "%s", prog); else { char *progname; @@ -288,12 +316,12 @@ run (char *server, mach_port_t *ports, task_t *task) "", 1, /* No env. */ default_dtable, MACH_MSG_TYPE_COPY_SEND, 3, ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX, - NULL, 0, /* No info in init ints. */ + default_ints, INIT_INT_MAX, NULL, 0, NULL, 0); if (!errno) break; - perror (prog); + error (0, errno, "%s", prog); } printf ("File name for server %s (or nothing to reboot): ", server); @@ -330,14 +358,14 @@ run_for_real (char *filename, char *args, int arglen, mach_port_t ctty) filename = buf; file = file_name_lookup (filename, O_EXEC, 0); if (!file) - perror (filename); + error (0, errno, "%s", filename); } while (!file); #else file = file_name_lookup (filename, O_EXEC, 0); if (!file) { - perror (filename); + error (0, errno, "%s", filename); return MACH_PORT_NULL; } #endif @@ -371,7 +399,7 @@ run_for_real (char *filename, char *args, int arglen, mach_port_t ctty) default_dtable, MACH_MSG_TYPE_COPY_SEND, 3, default_ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX, - NULL, 0, /* No info in init ints. */ + default_ints, INIT_INT_MAX, NULL, 0, NULL, 0); mach_port_deallocate (mach_task_self (), default_ports[INIT_PORT_PROC]); if (ctty != MACH_PORT_NULL) @@ -383,7 +411,7 @@ run_for_real (char *filename, char *args, int arglen, mach_port_t ctty) mach_port_deallocate (mach_task_self (), file); if (err) { - fprintf (stderr, "Cannot execute %s: %s.\n", filename, strerror (err)); + error (0, err, "Cannot execute %s", filename); return MACH_PORT_NULL; } return task; @@ -409,6 +437,8 @@ main (int argc, char **argv, char **envp) global_argv = argv; + system_state = INITIAL; + /* Parse the arguments */ bootstrap_args = 0; if (argc >= 2) @@ -470,6 +500,13 @@ main (int argc, char **argv, char **envp) default_dtable[1] = getdport (1); default_dtable[2] = getdport (2); + /* All programs we start should ignore job control stop signals. + That way Posix.1 B.2.2.2 is satisfied where it says that programs + not run under job control shells are protected. */ + default_ints[INIT_SIGIGN] = (sigmask (SIGTSTP) + | sigmask (SIGTTIN) + | sigmask (SIGTTOU)); + default_ports[INIT_PORT_BOOTSTRAP] = startup; run ("/hurd/proc", default_ports, &proctask); printf (" proc"); @@ -556,7 +593,7 @@ launch_core_servers (void) /* Give the bootstrap FS its proc and auth ports. */ errno = fsys_init (bootport, fsproc, MACH_MSG_TYPE_MOVE_SEND, authserver); if (errno) - perror ("fsys_init"); /* Not necessarily fatal. */ + error (0, errno, "fsys_init"); /* Not necessarily fatal. */ } /* Set up the initial value of the standard exec data. */ @@ -604,40 +641,42 @@ init_stdarrays () } -/* Start the single-user environment. This can only be done - when the core servers have fully started. We know that - startup_essential_task is the last thing they do before being - ready to handle requests, so we start this once all the necessary - servers have identified themselves that way. */ -void -launch_single_user () +/* 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. + + In any case, after the console has been opened, set it appropriately + in default_dtable. Also return a port right for the terminal. */ +file_t +open_console () { - char shell[1024]; - char terminal[] = "/hurd/term\0/tmp/console\0device\0console"; - mach_port_t term, shelltask; - char *termname; +#define TERMINAL_FIRST_TRY "/hurd/term\0/tmp/console\0device\0console" +#define TERMINAL_SECOND_TRY "/hurd/term\0/tmp\0device\0console" + static char *terminal; + int try; + mach_port_t term; + static char *termname; int fd; struct stat st; - error_t err; - - init_stdarrays (); - - printf ("Single-user environment:"); - fflush (stdout); + error_t err = 0; + + if (system_state != INITIAL) + { + term = file_name_lookup (termname, O_RDWR, 0); + return term; + } - /* Open the console. If we get something which is a terminal, then - we conclude that the filesystem has a proper translator for it, - and we're done. Otherwise, start /hurd/term on something inside - /tmp and use that. */ termname = _PATH_CONSOLE; term = file_name_lookup (termname, O_RDWR, 0); if (term != MACH_PORT_NULL) { err = io_stat (term, &st); if (err) - perror (termname); + error (0, err, "%s", termname); } - + + try = 1; if (term == MACH_PORT_NULL || err || st.st_fstype != FSTYPE_TERM) /* Start the terminal server ourselves. */ { @@ -649,18 +688,34 @@ launch_single_user () term = file_name_lookup (termname, flags | O_CREAT|O_NOTRANS, 0666); if (term == MACH_PORT_NULL) { - perror (termname); + error (0, errno, "%s", termname); return errno; } *underlying = term; *underlying_type = MACH_MSG_TYPE_COPY_SEND; - + return 0; } - termname = terminal + strlen (terminal) + 1; /* first arg is name */ + retry: + bootstrap_args |= RB_SINGLE; + if (try == 1) + { + terminal = TERMINAL_FIRST_TRY; + try = 2; + } + else if (try == 2) + { + terminal = TERMINAL_SECOND_TRY; + try = 3; + } + else + goto fail; + + 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, @@ -668,40 +723,44 @@ launch_single_user () &control); if (errno) { - perror (terminal); - goto fail; + error (0, errno, "%s", terminal); + goto retry; } - + errno = file_set_translator (term, 0, FS_TRANS_SET, 0, 0, 0, control, MACH_MSG_TYPE_MOVE_SEND); if (errno) { - perror (termname); - goto fail; + error (0, errno, "%s", termname); + goto retry; } mach_port_deallocate (mach_task_self (), term); - + /* Now repeat the open. */ term = file_name_lookup (termname, O_RDWR, 0); if (term == MACH_PORT_NULL) { - perror (termname); - goto fail; + error (0, errno, "%s", termname); + goto retry; } errno = io_stat (term, &st); if (errno) { - perror (termname); + error (0, errno, "%s", termname); term = MACH_PORT_NULL; - goto fail; + goto retry; } if (st.st_fstype != FSTYPE_TERM) { - fprintf (stderr, "Installed /tmp/console terminal failed\n"); + error (0, 0, "%s: Not a terminal", termname); term = MACH_PORT_NULL; - goto fail; + goto retry; } + + if (term) + error (0, 0, "Using temporary console %s", termname); } + fail: /* At this point either TERM is the console or it's null. If it's @@ -731,7 +790,31 @@ launch_single_user () default_dtable[1] = getdport (1); default_dtable[2] = getdport (2); } + else + error (0, 0, "Cannot open console"); + + return term; +} + + +/* Start the single-user environment. This can only be done + when the core servers have fully started. We know that + startup_essential_task is the last thing they do before being + ready to handle requests, so we start this once all the necessary + servers have identified themselves that way. */ +void +launch_single_user () +{ + char shell[1024]; + mach_port_t term, shelltask; + + printf ("Single-user environment:"); + fflush (stdout); + + term = open_console (); + system_state = SINGLE; + #if 0 printf ("Shell program [%s]: ", _PATH_BSHELL); if (! getstring (shell, sizeof shell)) @@ -749,6 +832,252 @@ launch_single_user () printf (" shell.\n"); fflush (stdout); } + +/* Run /etc/rc as a shell script. */ +void +process_rc_script () +{ + char *rcargs; + size_t rcargslen; + mach_port_t term; + task_t rctask; + + if (do_fastboot) + { + rcargs = malloc (rcargslen = sizeof _PATH_RUNCOM); + strcpy (rcargs, _PATH_RUNCOM); + } + else + { + rcargslen = asprintf (&rcargs, "%s%c%s", _PATH_RUNCOM, '\0', "autoboot"); + rcargslen++; /* final null */ + } + + term = open_console (); + + system_state = RUNCOM; + + rctask = run_for_real (rcargs, rcargs, rcargslen, term); + mach_port_deallocate (mach_task_self (), term); + if (rctask != MACH_PORT_NULL) + { + rc_pid = task2pid (rctask); + mach_port_deallocate (mach_task_self (), rctask); + } +} + +/* Start up multi-user state. */ +void +launch_multi_user () +{ + char *sessionargs; + size_t sessionargslen; + mach_port_t term; + task_t session_task; + + /* Right now, just run `/bin/login -aNOAUTH_TIMEOUT' */ + sessionargslen = asprintf (&sessionargs, "%s%c%s%n", _PATH_LOGIN, '\0', + "-aNOAUTH_TIMEOUT"); + sessionargslen++; /* final null */ + + term = open_console (); + system_state = MULTI; + + session_task = run_for_real (sessionargs, sessionargs, sessionargslen, term); + mach_port_deallocate (mach_task_self (), term); + if (session_task != MACH_PORT_NULL) + { + session_pid = task2pid (session_task); + mach_port_deallocate (mach_task_self (), session_task); + } +} + +/* Kill all the outstanding processes with SIGNO. Return 1 if + there were no tasks left to kill. */ +int +kill_everyone (int signo) +{ + pid_t pidbuf[100], *pids = pidbuf; + mach_msg_type_number_t i, npids = 100; + task_t tk; + struct ess_task *es; + mach_port_t msg; + int didany; + int nloops; + error_t err; + + for (nloops = 10; nloops; nloops--) + { + if (nloops < 9) + /* Give it a rest for folks to have a chance to die */ + sleep (1); + + didany = 0; + err = proc_getallpids (procserver, &pids, &npids); + if (!err) + { + for (i = 0; i < npids; i++) + { + if (pids[i] == 1 /* us */ + || pids[i] == 2 /* kernel */ + || pids[i] == 3 /* default pager for now XXX */) + continue; + + /* See if the task is essential */ + err = proc_pid2task (procserver, pids[i], &tk); + if (err) + continue; + + for (es = ess_tasks; es; es = es->next) + if (tk == es->task_port) + { + /* Skip this one */ + mach_port_deallocate (mach_task_self (), tk); + continue; + } + + /* Kill it */ + if (signo == SIGKILL) + { + task_terminate (tk); + didany = 1; + } + else + { + err = proc_getmsgport (procserver, pids[i], &msg); + if (err) + { + mach_port_deallocate (mach_task_self (), tk); + continue; + } + + didany = 1; + msg_sig_post (msg, signo, 0, tk); + mach_port_deallocate (mach_task_self (), tk); + } + } + } + if (pids != pidbuf) + vm_deallocate (mach_task_self (), (vm_address_t) pids, + npids * sizeof (pid_t)); + if (!didany) + return 1; + } + return 0; +} + +/* Kill outstanding multi-user sessions */ +void +kill_multi_user () +{ + int sigs[3] = {SIGHUP, SIGTERM, SIGKILL}; + int stage; + + /* Notify tasks that they are about to die. */ + notify_shutdown ("transition to single-user"); + + for (stage = 0; stage < 3; stage++) + if (kill_everyone (sigs[stage])) + break; + + if (stage == 3) + error (0, 0, "warning: some processes wouldn't die; `ps -axlM' advised"); +} + +/* SIGNO has arrived and has been validated. Do whatever work it + implies. */ +void +process_signal (int signo) +{ + switch (signo) + { + case SIGTERM: + if (system_state == MULTI) + { + /* Drop back to single user. */ + kill_multi_user (); + launch_single_user (); + } + break; + + case SIGHUP: + if (system_state == MULTI) + { + /* Should re-read ttys file here, but it's static and hard-coded + into launch_multi_user right now. */ + } + break; + + case SIGCHLD: + { + /* A child died. Find its status. */ + int status; + pid_t pid = waitpid (WAIT_ANY, &status, WNOHANG); + if (pid < 0) + error (0, errno, "waitpid"); + else if (pid == 0) + error (0, 0, "Spurious SIGCHLD"); + else if (pid == shell_pid && system_state == SINGLE) + { + if (WIFSIGNALED (status)) + { + error (0, 0, + "Single-user terminated abnormally (%s), restarting", + strsignal (WTERMSIG (status))); + launch_single_user (); + } + else if (WIFSTOPPED (status)) + { + error (0, 0, + "Single-user stopped (%s), killing and restarting", + strsignal (WSTOPSIG (status))); + kill (shell_pid, SIGKILL); + launch_single_user (); + } + else + { + do_fastboot = 1; + process_rc_script (); + } + } + else if (pid == rc_pid && system_state == RUNCOM) + { + if (WIFSIGNALED (status)) + { + error (0, 0, + "%s terminated abnormally (%s), going to single user mode", + _PATH_RUNCOM, strsignal (WTERMSIG (status))); + launch_single_user (); + } + else if (WIFSTOPPED (status)) + { + error (0, 0, + "%s stopped (%s), killing it and going to single user mode", + _PATH_RUNCOM, strsignal (WSTOPSIG (status))); + kill (rc_pid, SIGKILL); + launch_single_user (); + } + else if (WEXITSTATUS (status)) + launch_single_user (); + else + launch_multi_user (); + } + else if (pid == session_pid && system_state == MULTI) + /* XXX should handle separate sessions instead of only one... */ + launch_multi_user (); +#if 0 + else + error (0, 0, "Random child pid %d died (%d)", pid, status); +#endif + break; + } + + default: + break; + } +} + + kern_return_t @@ -816,7 +1145,7 @@ S_startup_essential_task (mach_port_t server, { struct ess_task *et; mach_port_t prev; - static int authinit, procinit, execinit, initdone; + static int authinit, procinit, execinit; if (credential != host_priv) return EPERM; @@ -849,7 +1178,7 @@ S_startup_essential_task (mach_port_t server, mach_port_deallocate (mach_task_self (), credential); - if (!initdone) + if (system_state == INITIAL) { if (!strcmp (name, "auth")) authinit = 1; @@ -864,8 +1193,12 @@ S_startup_essential_task (mach_port_t server, is ready for real startup to begin. */ startup_essential_task_reply (reply, replytype, 0); - launch_single_user (); - initdone = 1; + init_stdarrays (); + + if (bootstrap_args & RB_SINGLE) + launch_single_user (); + else + process_rc_script (); return MIG_NO_REPLY; } } @@ -910,9 +1243,7 @@ do_mach_notify_dead_name (mach_port_t notify, if (et->task_port == name) /* An essential task has died. */ { - printf ("Init crashing system; essential task %s died\n", - et->name); - fflush (stdout); + error (0, 0, "Crashing system; essential task %s died", et->name); crash_system (); } @@ -987,39 +1318,13 @@ S_msg_sig_post_untraced (mach_port_t msgport, { if (refport != mach_task_self ()) return EPERM; - - switch (signo) - { - case SIGCHLD: - { - /* A child died. Find its status. */ - int status; - pid_t pid = waitpid (WAIT_ANY, &status, WNOHANG); - if (pid < 0) - perror ("init: waitpid"); - else if (pid == 0) - fprintf (stderr, "init: Spurious SIGCHLD.\n"); - else if (pid == shell_pid) - { - fprintf (stderr, - "init: Single-user shell PID %d died (%d), restarting.\n", - pid, status); - launch_single_user (); - } -#if 0 - else - fprintf (stderr, "init: Random child PID %d died (%d).\n", - pid, status); -#endif - break; - } - - default: - break; - } - mach_port_deallocate (mach_task_self (), refport); - return 0; + + /* Reply immediately */ + msg_sig_post_untraced_reply (reply, reply_type, 0); + + process_signal (signo); + return MIG_NO_REPLY; } kern_return_t @@ -1027,7 +1332,15 @@ S_msg_sig_post (mach_port_t msgport, mach_port_t reply, mach_msg_type_name_t reply_type, int signo, mach_port_t refport) { - return S_msg_sig_post_untraced (msgport, reply, reply_type, signo, refport); + if (refport != mach_task_self ()) + return EPERM; + mach_port_deallocate (mach_task_self (), refport); + + /* Reply immediately */ + msg_sig_post_reply (reply, reply_type, 0); + + process_signal (signo); + return MIG_NO_REPLY; } |