From 33a7f9acbdaa75dd2f8638a3ff6589e0aea993c8 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 02/10] trans: add `startup-standalone' Provide a stripped-down version of the startup translator that supervises core servers and handles system shutdown. * trans/startup-standalone.c: New file. * trans/Makefile: Add `startup-standalone'. --- trans/Makefile | 9 +- trans/startup-standalone.c | 446 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 451 insertions(+), 4 deletions(-) create mode 100644 trans/startup-standalone.c diff --git a/trans/Makefile b/trans/Makefile index ce1eae7..5153361 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 startup-standalone 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 startup-standalone.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 startupServer.o HURDLIBS = ports netfs trivfs iohelp fshelp pipe ihash shouldbeinlibc LDLIBS += -lpthread password-LDLIBS = -lcrypt @@ -61,8 +61,9 @@ crash: crashServer.o crash_replyUser.o msgServer.o elfcore.o password: passwordServer.o streamio: device_replyServer.o proxy-defpager: default_pagerServer.o default_pagerUser.o +startup-standalone: startupServer.o startup_notifyUser.o -proxy-defpager crash password streamio: ../libports/libports.a ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a +proxy-defpager crash password streamio startup-standalone: ../libports/libports.a ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a fifo new-fifo: ../libpipe/libpipe.a fwd: ../libfshelp/libfshelp.a ../libports/libports.a hello-mt magic null ifsock fifo new-fifo firmlink: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a diff --git a/trans/startup-standalone.c b/trans/startup-standalone.c new file mode 100644 index 0000000..187a126 --- /dev/null +++ b/trans/startup-standalone.c @@ -0,0 +1,446 @@ +/* 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 "startup_notify_U.h" +#include "startup_reply_U.h" +#include "startup_S.h" +#include "notify_S.h" + +/* The privileged host control port. Used for authentication. */ +mach_port_t host_priv; + +/* 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") + +const char *argp_program_version = STANDARD_HURD_VERSION (startup-standalone); + +/* 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_IFREG | S_IRUSR | S_IRGRP | S_IROTH); + st->st_size = 0; +} + +error_t +trivfs_goaway (struct trivfs_control *cntl, int flags) +{ + exit (0); +} + +/* Options processing. We accept the same options on the command line + and from fsys_set_options. */ + +static const struct argp_option options[] = +{ + {"crash-debug", 'H', NULL, 0, "On system crash, go to 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; + 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 (crash_flags == RB_DEBUGGER) + err = argz_add (argz, argz_len, "--crash-debug"); + + return err; +} + +static const char doc[] = + "Supervise Hurd core servers and manage system shutdown"; + +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; +} + +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t bootstrap; + struct trivfs_control *fsys; + 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) + error (1, err, "Must be started as root"); + + 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, &fsys); + 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, fsys->pi.bucket, 0, + ¬ification); + if (err) + error (1, err, "ports_create_port"); + + /* Launch. */ + ports_manage_port_operations_one_thread (fsys->pi.bucket, demuxer, 0); + + return 0; +} + +/* This structure keeps track of each notified task. */ +struct ntfy_task + { + mach_port_t notify_port; + struct ntfy_task *next; + 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; + +/** System shutdown **/ + +/* Reboot the microkernel. */ +void +reboot_mach (int flags) +{ + error_t err; + printf ("%s: %sing Mach (flags %#x)...\n", + program_invocation_short_name, BOOT (flags), flags); + fflush (stdout); + sleep (5); + while ((err = host_reboot (host_priv, flags))) + error (0, err, "reboot"); + for (;;); +} + +/* 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; + 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) + puts ("(no longer present)"); + else if (err) + puts (strerror (err)); + else + puts ("done"); + fflush (stdout); + } +} + +/* Reboot the Hurd. */ +void +reboot_system (int flags) +{ + notify_shutdown (BOOT (flags)); + reboot_mach (flags); +} + +/* Reboot the Hurd, specifying that this is a crash. */ +void +crash_system (void) +{ + reboot_system (crash_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 (mach_port_t server, + 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; + if (credential != host_priv) + return EPERM; + + err = mach_port_deallocate (mach_task_self (), credential); + assert_perror (err); + + err = record_essential_task (name, task); + if (err) + return err; + + return 0; +} + +kern_return_t +S_startup_request_notification (mach_port_t server, + 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)); + nt->notify_port = notify; + nt->next = ntfy_tasks; + nt->name = strdup (name); + ntfy_tasks = nt; + return 0; +} + +kern_return_t +S_startup_procinit (startup_t bootstrap, + 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 (startup_t bootstrap, + 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); + crash_system (); + } + + 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 (mach_port_t server, + mach_port_t refpt, + int code) +{ + if (refpt != host_priv) + return EPERM; + + reboot_system (code); + for (;;); +} -- 2.1.4