From bd0301366daa289993ba16a4a65bb8768ef8ccf8 Mon Sep 17 00:00:00 2001 From: Justus Winter <4winter@informatik.uni-hamburg.de> Date: Tue, 13 Jan 2015 19:01:50 +0100 Subject: [PATCH hurd 1/2] trans: add shutdown translator Provide a stripped-down version of the startup translator that supervises core servers and handles server shutdown. * trans/shutdown.c: New file. * trans/Makefile: Add shutdown translator. --- trans/Makefile | 18 +- trans/shutdown.c | 514 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 528 insertions(+), 4 deletions(-) create mode 100644 trans/shutdown.c diff --git a/trans/Makefile b/trans/Makefile index 65b51d1..39dfa9b 100644 --- a/trans/Makefile +++ b/trans/Makefile @@ -21,14 +21,14 @@ makemode := servers targets = symlink firmlink ifsock magic null fifo new-fifo fwd crash \ password hello hello-mt streamio fakeroot proxy-defpager remap \ - mtab + mtab shutdown SRCS = ifsock.c symlink.c magic.c null.c fifo.c new-fifo.c fwd.c \ crash.c firmlink.c password.c hello.c hello-mt.c streamio.c \ - fakeroot.c proxy-defpager.c remap.c mtab.c + fakeroot.c proxy-defpager.c remap.c mtab.c shutdown.c OBJS = $(SRCS:.c=.o) fsysServer.o ifsockServer.o passwordServer.o \ crashServer.o crash_replyUser.o msgServer.o \ default_pagerServer.o default_pagerUser.o \ - device_replyServer.o elfcore.o + device_replyServer.o elfcore.o startup_notifyServer.o startupServer.o HURDLIBS = ports netfs trivfs iohelp fshelp pipe ihash shouldbeinlibc LDLIBS += -lpthread password-LDLIBS = -lcrypt @@ -66,8 +66,18 @@ streamio: device_replyServer.o symlink: fsysServer.o fakeroot: ../libnetfs/libnetfs.a +shutdown: startupServer.o startup_notifyUser.o + +# startupServer is used by the shutdown translator. +startup-MIGSFLAGS=\ + "-DSTARTUP_INTRAN=trivfs_protid_t trivfs_begin_using_protid (startup_t)" \ + "-DSTARTUP_INTRAN_PAYLOAD=trivfs_protid_t trivfs_begin_using_protid_payload" \ + "-DSTARTUP_DESTRUCTOR=trivfs_end_using_protid (trivfs_protid_t)" \ + "-DSTARTUP_IMPORTS=import \"../libtrivfs/mig-decls.h\";" + + fifo new-fifo: ../libpipe/libpipe.a -crash fifo firmlink hello hello-mt ifsock magic mtab new-fifo null password proxy-defpager remap streamio: ../libtrivfs/libtrivfs.a +crash fifo firmlink hello hello-mt ifsock magic mtab new-fifo null password proxy-defpager remap streamio shutdown: ../libtrivfs/libtrivfs.a $(targets): ../libfshelp/libfshelp.a \ ../libihash/libihash.a \ ../libiohelp/libiohelp.a \ diff --git a/trans/shutdown.c b/trans/shutdown.c new file mode 100644 index 0000000..6824cf6 --- /dev/null +++ b/trans/shutdown.c @@ -0,0 +1,514 @@ +/* Start and maintain hurd core servers and system run state + + Copyright (C) 1993-2015 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. */ + +/* Written by Michael I. Bushnell and Roland McGrath. */ + +/* This is probably more include files than I've ever seen before for + one file. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "startup_notify_U.h" +#include "startup_reply_U.h" +#include "startup_S.h" +#include "notify_S.h" + +/* The privileged host control port. Used for communication with the + kernel, and for authentication with our parent shutdown server. */ +mach_port_t host_priv; + +int system_mode; +int supervise; + +/* Time to wait for translators to shut down. */ +mach_msg_timeout_t timeout = 5000; + +/* Time to wait for privileged translators to shut down. */ +mach_msg_timeout_t privileged_timeout = 60000; + +/* We receive dead-name notifications here. */ +struct port_info *notification; + +/* host_reboot flags for when we crash. */ +static int crash_flags = RB_AUTOBOOT; + + + +#define BOOT(flags) ((flags & RB_HALT) ? "halt" : "reboot") + + + +/* This structure keeps track of each notified task. */ +struct ntfy_task + { + struct ntfy_task *next; + mach_port_t notify_port; + int privileged; + char *name; + }; + +/* This structure keeps track of each registered essential task. */ +struct ess_task + { + struct ess_task *next; + task_t task_port; + char *name; + }; + +/* These are linked lists of all of the registered items. */ +static struct ess_task *ess_tasks; +static struct ntfy_task *ntfy_tasks; + +/* Our trivfs control structure. */ +struct trivfs_control *control; + + + +const char *argp_program_version = STANDARD_HURD_VERSION (shutdown); + +/* Trivfs hooks. */ +int trivfs_fstype = FSTYPE_MISC; +int trivfs_fsid = 0; + +int trivfs_allow_open = 0; +int trivfs_support_read = 0; +int trivfs_support_write = 0; +int trivfs_support_exec = 0; + +void +trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) +{ + /* Mark the node as a read-only plain file. */ + st->st_mode &= ~(S_IFMT | ALLPERMS); + st->st_mode |= (S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH); + st->st_size = 0; +} + +error_t +trivfs_goaway (struct trivfs_control *cntl, int flags) +{ + if (ntfy_tasks && (flags & FSYS_GOAWAY_FORCE) == 0) + return EBUSY; + + exit (0); +} + +/* Options processing. We accept the same options on the command line + and from fsys_set_options. */ + +#define OPT_SUPERVISE -1 +#define OPT_SYSTEM -2 +static const struct argp_option options[] = +{ + {"supervise", OPT_SUPERVISE, NULL, 0, + "Enable supervision of essential servers", 0}, + {"system", OPT_SYSTEM, NULL, 0, + "Actually shut down the system (requires privileges)", 0}, + {"crash-debug", 'H', NULL, 0, + "On system crash, go to the kernel debugger", 0}, + {0} +}; + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + switch (opt) + { + case 'H': + crash_flags = RB_DEBUGGER; + break; + + case OPT_SUPERVISE: + supervise = 1; + break; + + case OPT_SYSTEM: + system_mode = 1; + break; + + default: + return ARGP_ERR_UNKNOWN; + case ARGP_KEY_INIT: + case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: + break; + } + return 0; +} + +/* This will be called from libtrivfs to help construct the answer + to an fsys_get_options RPC. */ +error_t +trivfs_append_args (struct trivfs_control *fsys, + char **argz, size_t *argz_len) +{ + error_t err = 0; + + if (! err && supervise) + err = argz_add (argz, argz_len, "--supervise"); + + if (! err && system_mode) + err = argz_add (argz, argz_len, "--system"); + + if (! err && crash_flags == RB_DEBUGGER) + err = argz_add (argz, argz_len, "--crash-debug"); + + return err; +} + +static const char doc[] = + "Supervise and shut down Hurd servers."; + +static struct argp argp = + { + .options = options, + .parser = parse_opt, + .args_doc = NULL, + .doc = doc, + }; + +/* Setting this variable makes libtrivfs use our argp to + parse options passed in an fsys_set_options RPC. */ +struct argp *trivfs_runtime_argp = &argp; + +static int +demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + mig_routine_t routine; + if ((routine = startup_server_routine (inp)) || + (routine = NULL, trivfs_demuxer (inp, outp))) + { + if (routine) + (*routine) (inp, outp); + return TRUE; + } + else + return FALSE; +} + +static int +isowner (trivfs_protid_t cred) +{ + return idvec_contains (cred->user->uids, getuid ()); +} + +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t bootstrap; + struct port_class *notification_class; + + /* We use the same argp for options available at startup + as for options we'll accept in an fsys_set_options RPC. */ + argp_parse (&argp, argc, argv, 0, 0, 0); + + err = get_privileged_ports (&host_priv, NULL); + if (err && system_mode) + error (1, err, "Must be started as root in system mode"); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "Must be started as a translator"); + + /* Reply to our parent */ + err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &control); + if (err) + error (3, err, "trivfs_startup"); + + err = mach_port_deallocate (mach_task_self (), bootstrap); + assert_perror (err); + + notification_class = ports_create_class (NULL, NULL); + if (! notification_class) + error (1, errno, "ports_create_class"); + + err = ports_create_port (notification_class, control->pi.bucket, 0, + ¬ification); + if (err) + error (1, err, "ports_create_port"); + + /* Launch. */ + ports_manage_port_operations_one_thread (control->pi.bucket, demuxer, 0); + + return 0; +} + + + +/** System shutdown **/ + +/* Reboot the microkernel. */ +void +reboot_mach (int flags) +{ + error_t err; + + if (system_mode) + { + fprintf (stderr, "%s: %sing Mach (flags %#x)...\n", + program_invocation_short_name, BOOT (flags), flags); + sleep (5); + while ((err = host_reboot (host_priv, flags))) + error (0, err, "reboot"); + for (;;); + } + + err = startup_reboot (control->underlying, host_priv, flags); + if (err) + error (1, err, "Failed to contact next shutdown server"); + exit (EXIT_FAILURE); +} + +/* Reboot the microkernel, specifying that this is a crash. */ +void +crash_mach (void) +{ + reboot_mach (crash_flags); +} + +/* Notify all tasks that have requested shutdown notifications. */ +void +notify_shutdown (const char *msg) +{ + struct ntfy_task *n; + + for (n = ntfy_tasks; n != NULL; n = n->next) + { + error_t err; + fprintf (stderr, "%s: notifying %s of %s...", + program_invocation_short_name, n->name, msg); + err = startup_dosync (n->notify_port, + n->privileged ? privileged_timeout : timeout); + if (err == MACH_SEND_INVALID_DEST) + fprintf (stderr, "(no longer present)\n"); + else if (err) + fprintf (stderr, "%s\n", strerror (err)); + else + fprintf (stderr, "done\n"); + } +} + +/* Reboot the Hurd. */ +void +reboot_system (int flags) +{ + notify_shutdown (BOOT (flags)); + reboot_mach (flags); +} + +/* Request a dead-name notification sent to our port. */ +static error_t +request_dead_name (mach_port_t name) +{ + error_t err; + mach_port_t prev; + err = mach_port_request_notification (mach_task_self (), name, + MACH_NOTIFY_DEAD_NAME, 1, + notification->port_right, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev); + if (! err && MACH_PORT_VALID (prev)) + mach_port_deallocate (mach_task_self (), prev); + return err; +} + +/* Record an essential task in the list. */ +static error_t +record_essential_task (const char *name, task_t task) +{ + error_t err; + struct ess_task *et; + /* Record this task as essential. */ + et = malloc (sizeof (struct ess_task)); + if (et == NULL) + return ENOMEM; + et->task_port = task; + et->name = strdup (name); + if (et->name == NULL) + { + free (et); + return ENOMEM; + } + et->next = ess_tasks; + ess_tasks = et; + + /* Dead-name notification on the task port will tell us when it dies. */ + err = request_dead_name (task); + if (err) + return err; + + return 0; +} + +kern_return_t +S_startup_essential_task (trivfs_protid_t cred, + mach_port_t reply, + mach_msg_type_name_t replytype, + task_t task, + mach_port_t excpt, + char *name, + mach_port_t credential) +{ + error_t err; + mach_port_deallocate (mach_task_self (), credential); + + if (! cred->isroot && ! isowner (cred)) + return EPERM; + + if (system_mode && credential != host_priv) + return EPERM; + + if (! supervise) + return EOPNOTSUPP; + + err = record_essential_task (name, task); + if (err) + return err; + + return 0; +} + +kern_return_t +S_startup_request_notification (trivfs_protid_t cred, + mach_port_t notify, + char *name) +{ + error_t err; + struct ntfy_task *nt; + + err = request_dead_name (notify); + if (err) + return err; + + /* Note that the ntfy_tasks list is kept in inverse order of the + calls; this is important. We need later notification requests + to get executed first. */ + nt = malloc (sizeof (struct ntfy_task)); + if (nt == NULL) + return ENOMEM; + nt->next = ntfy_tasks; + nt->notify_port = notify; + nt->privileged = cred->isroot; + nt->name = strdup (name); + ntfy_tasks = nt; + return 0; +} + +kern_return_t +S_startup_procinit (trivfs_protid_t cred, + mach_port_t reply, + mach_msg_type_name_t replyPoly, + process_t procserver, + mach_port_t *startuptask, + auth_t *auth, + mach_port_t *hostpriv, + mach_msg_type_name_t *hostprivPoly, + mach_port_t *devmaster, + mach_msg_type_name_t *devmasterPoly) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_startup_authinit (trivfs_protid_t cred, + mach_port_t reply, + mach_msg_type_name_t replyPoly, + mach_port_t auth, + mach_port_t *proc, + mach_msg_type_name_t *procPoly) +{ + return EOPNOTSUPP; +} + +error_t +ports_do_mach_notify_dead_name (struct port_info *pi, + mach_port_t dead_name) +{ + error_t err; + struct ntfy_task *nt, *pnt; + struct ess_task *et; + + if (!pi) + return EOPNOTSUPP; + + ports_dead_name (pi, dead_name); + + /* Drop gratuitous extra reference that the notification creates. */ + err = mach_port_deallocate (mach_task_self (), dead_name); + assert_perror (err); + + if (pi != notification) + return 0; + + for (et = ess_tasks; et != NULL; et = et->next) + if (et->task_port == dead_name) + /* An essential task has died. */ + { + error (0, 0, "Crashing system; essential task %s died", et->name); + reboot_system (crash_flags); + } + + for (nt = ntfy_tasks, pnt = NULL; nt != NULL; pnt = nt, nt = nt->next) + if (nt->notify_port == dead_name) + { + /* Someone who wanted to be notified is gone. */ + err = mach_port_deallocate (mach_task_self (), dead_name); + assert_perror (err); + if (pnt != NULL) + pnt->next = nt->next; + else + ntfy_tasks = nt->next; + free (nt); + + return 0; + } + + return 0; +} + +kern_return_t +S_startup_reboot (trivfs_protid_t cred, + mach_port_t refpt, + int code) +{ + mach_port_deallocate (mach_task_self (), refpt); + + if (! cred->isroot && ! isowner (cred)) + return EPERM; + + if (system_mode && refpt != host_priv) + return EPERM; + + reboot_system (code); + for (;;); +} -- 2.1.4