summaryrefslogtreecommitdiff
path: root/reincarnation.c
diff options
context:
space:
mode:
Diffstat (limited to 'reincarnation.c')
-rw-r--r--reincarnation.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/reincarnation.c b/reincarnation.c
new file mode 100644
index 0000000..8f333cf
--- /dev/null
+++ b/reincarnation.c
@@ -0,0 +1,373 @@
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <error.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <error.h>
+#include <argz.h>
+#include <hurd/fshelp.h>
+#include <hurd/process.h>
+
+#include <hurd/lookup.h>
+#include <hurd/fsys.h>
+#include <mach.h>
+#include <mach/notify.h>
+
+#include <version.h>
+
+#include "reincarnation.h"
+#include "reincarnation_S.h"
+
+const char const *argp_program_version = STANDARD_HURD_VERSION (reincarnation);
+
+/* 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_REINCARNATION + 1];
+mach_port_t fds[STDERR_FILENO + 1];
+int ints[INIT_INT_MAX];
+
+#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)
+ 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};
+
+mach_port_t reincarnation_port;
+error_t
+start_child (void);
+
+static int
+demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ extern int notify_server ();
+ extern int reincarnation_server ();
+
+ return (notify_server (inp, outp) ||
+ reincarnation_server (inp, outp));
+}
+
+error_t
+prepare_child (void)
+{
+ for (int i = 0; i < INIT_PORT_REINCARNATION; 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 ();
+ ports[INIT_PORT_REINCARNATION] = reincarnation_port;
+ fds[STDERR_FILENO] = getdport (STDERR_FILENO);
+
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+
+ 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");
+
+ mach_msg_type_name_t acquired_type;
+ err = mach_port_extract_right (mach_task_self (),
+ reincarnation_port,
+ MACH_MSG_TYPE_MAKE_SEND,
+ &reincarnation_port,
+ &acquired_type);
+ if (err)
+ error (3, err, "mach_port_extract_right");
+
+ prepare_child ();
+
+ mach_port_t registered_ports[] = { reincarnation_port, };
+ err = mach_ports_register (mach_task_self (),
+ &registered_ports, 1);
+ if (err)
+ error (5, err, "mach_port_register");
+
+ err = start_child ();
+ if (err)
+ error (5, err, "starting 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;
+ }
+
+ 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_REINCARNATION,
+ ints, INIT_INT_MAX,
+ geteuid (),
+ timeout, &active_control);
+ if (err)
+ return 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);
+
+ err = file_set_translator (node,
+ passive_flags, active_flags, goaway_flags,
+ argz, argz_len,
+ active_control, MACH_MSG_TYPE_COPY_SEND);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+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;
+
+ error (0, 0, "child died");
+
+ err = start_child ();
+ if (err)
+ error (0, err, "starting child");
+
+ return 0;
+}
+
+data_t stored_image;
+size_t stored_image_len;
+
+error_t
+S_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_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;
+}
+
+/* Stubs for unused notification RPCs. */
+
+kern_return_t
+do_mach_notify_port_destroyed (mach_port_t notify,
+ mach_port_t rights)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_send_once (mach_port_t notify)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_no_senders (mach_port_t port, mach_port_mscount_t mscount)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_port_deleted (mach_port_t notify,
+ mach_port_t name)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_msg_accepted (mach_port_t notify,
+ mach_port_t name)
+{
+ return EOPNOTSUPP;
+}