/* 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 "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_MAX];
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_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
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");
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 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_MAX,
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;
}