/* A reincarnation server. Copyright (C) 2013 Free Software Foundation, Inc. Written by Justus Winter <4winter@informatik.uni-hamburg.de> This file might one day be a part of the GNU Hurd. 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "priv.h" #include "reincarnation.h" #include "reincarnation_S.h" const char const *argp_program_version = STANDARD_HURD_VERSION (reincarnation); /* Our bootstrap port in case we are called as a translator. */ mach_port_t our_bootstrap; /* The number of failures. */ unsigned int failures; /* XXX currently we handle all messages on this port. */ mach_port_t reincarnation_port; /* The filesystem node we're putting a translator on. */ char *node_name = NULL; file_t node; /* The translator's arg vector, in '\0' separated format. */ char *argz = NULL; size_t argz_len = 0; /* The control port for any active translator we start up. */ fsys_t active_control = MACH_PORT_NULL; task_t child_task; int active_flags = FS_TRANS_SET; int passive_flags = 0; int lookup_flags = O_NOTRANS; int goaway_flags = 0; /* Various option flags. */ int passive = 0, active = 0, keep_active = 0, do_pause = 0, kill_active = 0, orphan = 0; int excl = 0; #define DEFAULT_TIMEOUT 60 int timeout = DEFAULT_TIMEOUT * 1000; /* ms */ /* child stuff */ mach_port_t ports[INIT_PORT_MAX]; mach_port_t fds[STDERR_FILENO + 1]; int ints[INIT_INT_MAX]; /* Forward declarations. */ int translator_mode (void); error_t start_child (void); error_t chainload_translator (char *name, char *argz, int argz_len, mach_port_t *fds, mach_msg_type_name_t fds_type, int fds_len, mach_port_t *ports, mach_msg_type_name_t ports_type, int ports_len, int *ints, int ints_len, uid_t owner_uid, task_t *task); /* Command line parsing. */ #define _STRINGIFY(arg) #arg #define STRINGIFY(arg) _STRINGIFY (arg) static const struct argp_option const options[] = { {"create", 'c', 0, 0, "Create NODE if it doesn't exist" }, {"pause", 'P', 0, 0, "When starting an active translator, prompt and" " wait for a newline on stdin before completing the startup handshake"}, {"goaway", 'g', 0, 0, "Ask the active translator to go away"}, {"keep-active", 'k', 0, 0, "Leave any existing active translator running"}, {0,0,0,0, "When an active translator is told to go away:"}, {"recursive", 'R', 0, 0, "Shutdown its children too"}, {"force", 'f', 0, 0, "Ask it to ignore current users and shutdown " "anyway." }, {"nosync", 'S', 0, 0, "Don't sync it before killing it"}, {0, 0} }; static const char const args_doc[] = "NODE [TRANSLATOR ARG...]"; static const char const doc[] = "Set the active translator on NODE and provide " "reincarnation services for that translator."; /* Parse our options... */ static error_t parse_opt (int key, char *arg, struct argp_state *state) { switch (key) { case 'P': do_pause = 1; break; case 'c': lookup_flags |= O_CREAT; break; case 'L': lookup_flags &= ~O_NOTRANS; break; case 'R': goaway_flags |= FSYS_GOAWAY_RECURSE; break; case 'S': goaway_flags |= FSYS_GOAWAY_NOSYNC; break; case 'f': goaway_flags |= FSYS_GOAWAY_FORCE; break; /* Use atof so the user can specifiy fractional timeouts. */ case 't': timeout = atof (arg) * 1000.0; break; case ARGP_KEY_ARG: if (state->arg_num == 0 && ! translator_mode ()) node_name = arg; else /* command */ { error_t err = argz_create (state->argv + state->next - 1, &argz, &argz_len); if (err) error(3, err, "Can't create options vector"); state->next = state->argc; /* stop parsing */ } break; case ARGP_KEY_NO_ARGS: argp_usage (state); return EINVAL; default: return ARGP_ERR_UNKNOWN; } return 0; } struct argp argp = {options, parse_opt, args_doc, doc}; static int demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) { extern int notify_server (mach_msg_header_t *, mach_msg_header_t *); extern int reincarnation_server (mach_msg_header_t *, mach_msg_header_t *); extern int fsys_server (mach_msg_header_t *, mach_msg_header_t *); return (notify_server (inp, outp) || reincarnation_server (inp, outp) || fsys_server (inp, outp)); } error_t prepare_child (void) { for (int i = 0; i < INIT_PORT_MAX; i++) ports[i] = MACH_PORT_NULL; for (int i = 0; i < STDERR_FILENO + 1; i++) fds[i] = MACH_PORT_NULL; memset (ints, 0, INIT_INT_MAX * sizeof (int)); ports[INIT_PORT_CWDIR] = getcwdir (); ports[INIT_PORT_CRDIR] = getcrdir (); ports[INIT_PORT_AUTH] = getauth (); fds[STDERR_FILENO] = getdport (STDERR_FILENO); return 0; } int translator_mode (void) { return our_bootstrap != MACH_PORT_NULL; } int main (int argc, char **argv) { error_t err; /* See if we are used as translator. */ task_get_bootstrap_port (mach_task_self (), &our_bootstrap); if (translator_mode ()) { active_flags |= FS_TRANS_ORPHAN; } argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &reincarnation_port); if (err) error (5, err, "mach_port_allocate"); err = mach_port_insert_right (mach_task_self (), reincarnation_port, reincarnation_port, MACH_MSG_TYPE_MAKE_SEND); if (err) error (3, err, "mach_port_insert_right"); prepare_child (); err = reincarnation_set_port (mach_task_self (), reincarnation_port); if (err) error (5, err, "reincarnation_set_port"); err = start_child (); if (err) error (5, err, "starting initial child"); while (1) { err = mach_msg_server (demuxer, 0, reincarnation_port); if (err) error (6, err, "mach_msg_server"); } } error_t start_child (void) { error_t err; error (0, 0, "Starting child: %s", argz); /* Error during file lookup; we use this to avoid duplicating error messages. */ error_t open_err = 0; /* The callback to start_translator opens NODE as a side effect. */ error_t open_node (int flags, mach_port_t *underlying, mach_msg_type_name_t *underlying_type, task_t task, void *cookie) { child_task = task; if (do_pause) { fprintf (stderr, "Translator pid: %d\nPausing...", task2pid (task)); getchar (); } if (node == MACH_PORT_NULL) { node = file_name_lookup (node_name, flags | lookup_flags, 0666); if (node == MACH_PORT_NULL) { open_err = errno; return open_err; } } *underlying = node; *underlying_type = MACH_MSG_TYPE_COPY_SEND; return 0; } if (translator_mode ()) { ports[INIT_PORT_BOOTSTRAP] = reincarnation_port; /* XXX */ err = chainload_translator (argz, argz, argz_len, fds, MACH_MSG_TYPE_COPY_SEND, STDERR_FILENO + 1, ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX, ints, INIT_INT_MAX, geteuid (), &child_task); } else { err = fshelp_start_translator_long (open_node, NULL, argz, argz, argz_len, fds, MACH_MSG_TYPE_COPY_SEND, STDERR_FILENO + 1, ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX, ints, INIT_INT_MAX, geteuid (), timeout, &active_control); if (err) return err; err = install_translator (); if (err) return err; err = register_dead_name_notification (); } return err; } error_t register_dead_name_notification (void) { error_t err; /* Ask to be told if TASK dies. */ mach_port_t prev_notify; err = mach_port_request_notification (mach_task_self (), active_control, MACH_NOTIFY_DEAD_NAME, 0, reincarnation_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_notify); if (err) return err; if (prev_notify != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), prev_notify); return err; } error_t install_translator (void) { error_t err = file_set_translator (node, passive_flags, active_flags, goaway_flags, argz, argz_len, active_control, MACH_MSG_TYPE_COPY_SEND); return err; } error_t chainload_translator (char *name, char *argz, int argz_len, mach_port_t *fds, mach_msg_type_name_t fds_type, int fds_len, mach_port_t *ports, mach_msg_type_name_t ports_type, int ports_len, int *ints, int ints_len, uid_t owner_uid, task_t *task) { error_t err; /* Find the translator itself. */ file_t executable = file_name_lookup (name, O_EXEC, 0); if (executable == MACH_PORT_NULL) return errno; /* Create the task for the translator. */ err = task_create (mach_task_self (), #ifdef KERN_INVALID_LEDGER NULL, 0, /* OSF Mach */ #endif 0, task); if (err) goto lose; /* XXX 25 is BASEPRI_USER, which isn't exported by the kernel. Ideally, nice values should be used, perhaps with a simple wrapper to convert them to Mach priorities. */ err = task_priority (*task, 25, FALSE); if (err) goto lose; /* Designate TASK as our child and set it's owner accordingly. */ mach_port_t proc = getproc (); proc_child (proc, *task); mach_port_t childproc; err = proc_task2proc (proc, *task, &childproc); mach_port_deallocate (mach_task_self (), proc); if (err) goto lose; err = proc_setowner (childproc, owner_uid, owner_uid == (uid_t) -1); mach_port_deallocate (mach_task_self (), childproc); if (err) goto lose; /* Try and exec the translator in TASK... */ err = file_exec (executable, *task, EXEC_DEFAULTS, argz, argz_len, 0, 0, fds, fds_type, fds_len, ports, ports_type, ports_len, ints, ints_len, 0, 0, 0, 0); if (err) { task_terminate (*task); goto lose; } lose: return err; } kern_return_t do_mach_notify_dead_name (mach_port_t notify, mach_port_t name) { error_t err; mach_port_deallocate (mach_task_self (), name); if (name != active_control) return EPERM; failures += 1; error (0, 0, "Child died (%u failure%s)", failures, failures > 1? "s": ""); err = start_child (); if (err) error (0, err, "starting child"); return 0; } data_t stored_image; size_t stored_image_len; error_t S_reincarnation_reincarnate (mach_port_t server, data_t *image, mach_msg_type_number_t *imageCnt) { *image = stored_image; *imageCnt = stored_image_len; return 0; } error_t S_reincarnation_checkpoint (mach_port_t server, data_t image, mach_msg_type_number_t imageCnt) { free (stored_image); stored_image = malloc (imageCnt); if (! stored_image) return ENOMEM; memcpy (stored_image, image, imageCnt); stored_image_len = imageCnt; return 0; } error_t S_reincarnation_goaway (mach_port_t server) { exit (0); }