aboutsummaryrefslogtreecommitdiffstats
path: root/src/mds-respawn.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mds-respawn.c')
-rw-r--r--src/mds-respawn.c279
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;
+}
+