diff options
author | Justus Winter <4winter@informatik.uni-hamburg.de> | 2013-08-01 09:00:58 +0200 |
---|---|---|
committer | Justus Winter <4winter@informatik.uni-hamburg.de> | 2013-08-01 09:00:58 +0200 |
commit | 06c3b46e273598a94480df39d01617c14764ee39 (patch) | |
tree | e2984f9a285a521b07343873afc86d4954cc1605 |
Initial commit of the reincarnation prototype
-rw-r--r-- | Makefile | 39 | ||||
-rw-r--r-- | persistent-hello.c | 336 | ||||
-rw-r--r-- | reincarnation.c | 373 | ||||
-rw-r--r-- | reincarnation.defs | 36 | ||||
-rw-r--r-- | reincarnation.h | 29 |
5 files changed, 813 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..32c7be1 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +# Reincarnation services for translators. +# +# 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/>. + +dir := reincarnation +makemode := servers + +SRCS = reincarnation.c persistent-hello.c +OBJS = $(SRCS:.c=.o) \ + notifyServer.o \ + reincarnationServer.o \ + reincarnationUser.o \ + +targets = reincarnation persistent-hello +HURDLIBS = fshelp trivfs ihash shouldbeinlibc + +include ../Makeconf + +reincarnation: notifyServer.o reincarnationServer.o ../libfshelp/libfshelp.a +persistent-hello: reincarnationUser.o ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a + +$(targets): %: %.o diff --git a/persistent-hello.c b/persistent-hello.c new file mode 100644 index 0000000..0efc60f --- /dev/null +++ b/persistent-hello.c @@ -0,0 +1,336 @@ +/* persistent-hello.c - A trivial single-file translator using + reincarnation services. + + Based upon: hello.c - A trivial single-file translator + + Copyright (C) 1998,1999,2001,02,2006 Free Software Foundation, Inc. + Gordon Matzigkeit <gord@fig.org>, 1999 + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#define _GNU_SOURCE 1 + +#include <hurd/trivfs.h> +#include <stdio.h> +#include <stdlib.h> +#include <argp.h> +#include <argz.h> +#include <error.h> +#include <string.h> +#include <fcntl.h> +#include <sys/mman.h> + +#include <version.h> + +#include "reincarnation.h" +#include "reincarnation_U.h" + +static mach_port_t reincarnation; + +const char *argp_program_version = STANDARD_HURD_VERSION (persistent-hello); + +/* The message we return when we are read. */ +static const char hello[] = "Hello, world!\n"; +static char *contents = (char *) hello; +static size_t contents_len = sizeof hello - 1; + +/* Trivfs hooks. */ +int trivfs_fstype = FSTYPE_MISC; +int trivfs_fsid = 0; + +int trivfs_allow_open = O_READ; + +int trivfs_support_read = 1; +int trivfs_support_write = 0; +int trivfs_support_exec = 0; + +/* NOTE: This example is not robust: it is possible to trigger some + assertion failures because we don't implement the following: + + $ cd /src/hurd/libtrivfs + $ grep -l 'assert.*!trivfs_support_read' *.c | + xargs grep '^trivfs_S_' | sed 's/^[^:]*:\([^ ]*\).*$/\1/' + trivfs_S_io_get_openmodes + trivfs_S_io_clear_some_openmodes + trivfs_S_io_set_some_openmodes + trivfs_S_io_set_all_openmodes + trivfs_S_io_readable + trivfs_S_io_select + $ + + For that reason, you should run this as an active translator + `settrans -ac testnode /path/to/thello' so that you can see the + error messages when they appear. */ + +/* A hook for us to keep track of the file descriptor state. */ +struct open +{ + off_t offs; +}; + +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 = contents_len; +} + +error_t +trivfs_goaway (struct trivfs_control *cntl, int flags) +{ + exit (0); +} + + +static error_t +open_hook (struct trivfs_peropen *peropen) +{ + struct open *op = malloc (sizeof (struct open)); + if (op == NULL) + return ENOMEM; + + /* Initialize the offset. */ + op->offs = 0; + peropen->hook = op; + return 0; +} + + +static void +close_hook (struct trivfs_peropen *peropen) +{ + free (peropen->hook); +} + + +/* Read data from an IO object. If offset is -1, read from the object + maintained file pointer. If the object is not seekable, offset is + ignored. The amount desired to be read is in AMOUNT. */ +error_t +trivfs_S_io_read (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + char **data, mach_msg_type_number_t *data_len, + loff_t offs, mach_msg_type_number_t amount) +{ + struct open *op; + + /* Deny access if they have bad credentials. */ + if (! cred) + return EOPNOTSUPP; + else if (! (cred->po->openmodes & O_READ)) + return EBADF; + + /* Get the offset. */ + op = cred->po->hook; + if (offs == -1) + offs = op->offs; + + /* Prune the amount they want to read. */ + if (offs > contents_len) + offs = contents_len; + if (offs + amount > contents_len) + amount = contents_len - offs; + + if (amount > 0) + { + /* Possibly allocate a new buffer. */ + if (*data_len < amount) + { + *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (*data == MAP_FAILED) + return ENOMEM; + } + + /* Copy the constant data into the buffer. */ + memcpy ((char *) *data, contents + offs, amount); + + /* Update the saved offset. */ + op->offs += amount; + } + + *data_len = amount; + return 0; +} + + +/* Change current read/write offset */ +error_t +trivfs_S_io_seek (struct trivfs_protid *cred, + mach_port_t reply, mach_msg_type_name_t reply_type, + off_t offs, int whence, off_t *new_offs) +{ + struct open *op; + error_t err = 0; + if (! cred) + return EOPNOTSUPP; + + op = cred->po->hook; + switch (whence) + { + case SEEK_CUR: + offs += op->offs; + goto check; + case SEEK_END: + offs += contents_len; + case SEEK_SET: + check: + if (offs >= 0) + { + *new_offs = op->offs = offs; + break; + } + default: + err = EINVAL; + } + + return err; +} + + +/* If this variable is set, it is called every time a new peropen + structure is created and initialized. */ +error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook; + +/* If this variable is set, it is called every time a peropen structure + is about to be destroyed. */ +void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook; + + +/* Options processing. We accept the same options on the command line + and from fsys_set_options. */ + +static const struct argp_option options[] = +{ + {"contents", 'c', "STRING", 0, "Specify the contents of the virtual file"}, + {0} +}; + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + switch (opt) + { + default: + return ARGP_ERR_UNKNOWN; + case ARGP_KEY_INIT: + case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: + break; + + case 'c': + { + char *new = strdup (arg); + if (new == NULL) + return ENOMEM; + if (contents != hello) + free (contents); + contents = new; + contents_len = strlen (new); + + if (reincarnation != MACH_PORT_NULL) + { + error_t err = checkpoint (reincarnation, contents, contents_len); + if (err) + error (0, err, "checkpoint"); + } + + 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; + char *opt; + + if (asprintf (&opt, "--contents=%s", contents) < 0) + return ENOMEM; + + err = argz_add (argz, argz_len, opt); + + free (opt); + + return err; +} + +static struct argp hello_argp = +{ options, parse_opt, 0, "A translator providing a warm greeting." }; + +/* Setting this variable makes libtrivfs use our argp to + parse options passed in an fsys_set_options RPC. */ +struct argp *trivfs_runtime_argp = &hello_argp; + + +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t bootstrap; + struct trivfs_control *fsys; + + /* We use the same argp for options available at startup + as for options we'll accept in an fsys_set_options RPC. */ + argp_parse (&hello_argp, argc, argv, 0, 0, 0); + + 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); + mach_port_deallocate (mach_task_self (), bootstrap); + if (err) + error (3, err, "trivfs_startup"); + + /* Get reincarnation image. */ + mach_port_t *registered_ports = NULL; + size_t registered_ports_len = 0; + err = mach_ports_lookup (mach_task_self (), + ®istered_ports, ®istered_ports_len); + if (err) + error (5, err, "mach_port_lookup"); + + reincarnation = registered_ports[0]; + + char *image = NULL; + size_t image_len = 0; + err = reincarnate (reincarnation, &image, &image_len); + if (err) + error (3, err, "getting reincarnation image"); + + if (image_len) + { + free (contents); + contents = malloc (image_len); + if (! contents) + error (3, ENOMEM, "malloc"); + + memcpy (contents, image, image_len); + contents_len = image_len; + } + + /* Launch. */ + ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, 0); + + return 0; +} 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 (), + ®istered_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; +} diff --git a/reincarnation.defs b/reincarnation.defs new file mode 100644 index 0000000..cb47680 --- /dev/null +++ b/reincarnation.defs @@ -0,0 +1,36 @@ +/* Reincarnation services for translators. + + 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/>. */ + +subsystem reincarnation 36270; + +#include <hurd/hurd_types.defs> + +#ifdef FILE_IMPORTS +FILE_IMPORTS +#endif + +routine reincarnate ( + server: mach_port_t; + out image: data_t); + +routine checkpoint ( + server: mach_port_t; + in image: data_t); diff --git a/reincarnation.h b/reincarnation.h new file mode 100644 index 0000000..19a3a8d --- /dev/null +++ b/reincarnation.h @@ -0,0 +1,29 @@ +/* Reincarnation services for translators. + + 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/>. */ + +#ifndef __REINCARNATION_H__ +#define __REINCARNATION_H__ + +#include <hurd.h> + +#define INIT_PORT_REINCARNATION INIT_PORT_MAX + +#endif /* __REINCARNATION_H__ */ |