diff options
Diffstat (limited to 'src/mds-respawn.c')
-rw-r--r-- | src/mds-respawn.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/src/mds-respawn.c b/src/mds-respawn.c new file mode 100644 index 0000000..9f9dc78 --- /dev/null +++ b/src/mds-respawn.c @@ -0,0 +1,279 @@ +/** + * mds — A micro-display server + * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) + * + * 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 3 of the License, 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 "mds-respawn.h" + +#include <libmdsserver/macros.h> +#include <libmdsserver/util.h> + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + + +/** + * This variable should declared by the actual server implementation. + * It must be configured before `main` is invoked. + * + * This tells the server-base how to behave + */ +server_characteristics_t server_characteristics = + { + .require_privileges = 0, + .require_display = 0, + .require_respawn_info = 1, + .sanity_check_argc = 0 + }; + + + +/** + * Do not respawn crashed servers that did not live this many seconds + */ +static int interval = 5; + +/** + * The number of servers managed by this process + */ +static size_t servers = 0; + +/** + * Command line arguments, for each server — concatenated, with NULL termination + */ +static const char** commands_args = NULL; + +/** + * Mapping elements in `commands_args` that are the first + * argument for each server to run + */ +static const char*** commands = NULL; + +/** + * States of managed servers + */ +static server_state_t* states = NULL; + + + +/** + * Parse command line arguments + * + * @return Non-zero on error + */ +int parse_cmdline(void) +{ + int i; + size_t j, args = 0, stack = 0; + for (i = 1; i < argc; i++) + { + char* arg = argv[i]; + if (startswith(arg, "--alarm=")) /* Schedule an alarm signal for forced abort. */ + alarm((unsigned)min(atoi(arg + strlen("--alarm=")), 60)); /* At most 1 minute. */ + else if (startswith(arg, "--interval=")) + interval = min(atoi(arg + strlen("--interval=")), 60); /* At most 1 minute. */ + else if (strequals(arg, "--re-exec")) /* Re-exec state-marshal. */ + is_reexec = 1; + else if (strequals(arg, "{")) + servers += stack++ == 0 ? 1 : 0; + else if (strequals(arg, "}")) + { + exit_if (stack-- == 0, eprint("Terminating non-started command, aborting");); + exit_if (stack == 0 && strequals(argv[i - 1], "{"), + eprint("Zero argument command specified, aborting");); + } + else if (stack == 0) + eprintf("Unrecognised option: %s, did you forget `='?", arg); + else + args++; + } + if (is_reexec) + { + is_respawn = 1; + eprint("re-exec performed."); + } + + exit_if (stack > 0, eprint("Non-terminated command specified, aborting");); + exit_if (servers == 0, eprint("No programs to spawn, aborting");); + + fail_if (xmalloc(commands_args, args + servers, char*)); + fail_if (xmalloc(commands, servers, char**)); + fail_if (xmalloc(states, servers, server_state_t)); + + for (i = 1, args = j = 0; i < argc; i++) + { + char* arg = argv[i]; + if (strequals(arg, "}")) commands_args[args++] = --stack == 0 ? NULL : arg; + else if (stack > 0) commands_args[args++] = arg; + else if (strequals(arg, "{") && (stack++ == 0)) + commands[j++] = commands_args + args; + } + + return 0; + + pfail: + perror(*argv); + return 1; +} + + +/** + * This function is called when a signal that + * signals the program to respawn all + * `DEAD_AND_BURIED` server is received + * + * @param signo The signal that has been received + */ +static void received_revive(int signo) +{ + /* TODO */ + (void) signo; + eprint("revive signal received."); +} + + +/** + * This function will be invoked before `initialise_server` (if not re-exec:ing) + * or before `unmarshal_server` (if re-exec:ing) + * + * @return Non-zero on error + */ +int preinitialise_server(void) +{ + /* Make the server revive all `DEAD_AND_BURIED` servers on SIGUSR2. */ + fail_if (xsigaction(SIGUSR2, received_revive) < 0); + + return 0; + pfail: + perror(*argv); + return 1; +} + + +/** + * This function should initialise the server, + * and it not invoked after a re-exec. + * + * @return Non-zero on error + */ +int initialise_server(void) +{ + size_t i; + for (i = 0; i < servers; i++) + { + states[i].pid = 0; + states[i].state = UNBORN; + } + return 0; +} + + +/** + * This function will be invoked after `initialise_server` (if not re-exec:ing) + * or after `unmarshal_server` (if re-exec:ing) + * + * @return Non-zero on error + */ +int postinitialise_server(void) +{ + size_t i, j; + + /* TODO Spawn `UNBORN` servers. */ + + for (i = j = 0; j < servers; i++) + if (commands_args[i] == NULL) + j++; + else if (strequals(commands_args[i], "--initial-spawn")) + commands_args[i] = "--respawn"; + + /* TODO Spawn `DEAD` and `DEAD_AND_BURIED` servers. */ + + return 0; +} + + +/** + * Calculate the number of bytes that will be stored by `marshal_server` + * + * On failure the program should `abort()` or exit by other means. + * However it should not be possible for this function to fail. + * + * @return The number of bytes that will be stored by `marshal_server` + */ +size_t marshal_server_size(void) +{ + /* TODO */ + return 0; +} + + +/** + * Marshal server implementation specific data into a buffer + * + * @param state_buf The buffer for the marshalled data + * @return Non-zero on error + */ +int marshal_server(char* state_buf) +{ + /* TODO */ + (void) state_buf; + return 0; +} + + +/** + * Unmarshal server implementation specific data and update the servers state accordingly + * + * On critical failure the program should `abort()` or exit by other means. + * That is, do not let `reexec_failure_recover` run successfully, if it unrecoverable + * error has occurred or one severe enough that it is better to simply respawn. + * + * @param state_buf The marshalled data that as not been read already + * @return Non-zero on error + */ +int unmarshal_server(char* state_buf) +{ + /* TODO */ + (void) state_buf; + return 0; +} + + +/** + * Attempt to recover from a re-exec failure that has been + * detected after the server successfully updated it execution image + * + * @return Non-zero on error + */ +int reexec_failure_recover(void) +{ + /* TODO */ + return 0; +} + + +/** + * Perform the server's mission + * + * @return Non-zero on error + */ +int master_loop(void) +{ + /* TODO */ + return 0; +} + |