diff options
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | src/libmdsserver/util.c | 6 | ||||
-rw-r--r-- | src/mds-base.c | 53 | ||||
-rw-r--r-- | src/mds-base.h | 40 | ||||
-rw-r--r-- | src/mds-respawn.c | 279 | ||||
-rw-r--r-- | src/mds-respawn.h | 75 | ||||
-rw-r--r-- | src/mds-server/mds-server.c | 6 |
7 files changed, 450 insertions, 19 deletions
@@ -76,7 +76,7 @@ LIBOBJ = linked-list hash-table fd-table mds-message util # Build rules. .PHONY: all -all: obj/mds-base.o bin/mds bin/mds-server bin/libmdsserver.so +all: obj/mds-base.o bin/mds bin/mds-respawn bin/mds-server bin/libmdsserver.so MDS_SERVER_OBJ_ = mds-server interception-condition client multicast \ @@ -92,9 +92,13 @@ ifeq ($(DEBUG),y) cp test.d/mds-server $@ endif -bin/%: obj/%.o bin/libmdsserver.so +bin/%: obj/%.o obj/mds-base.o bin/libmdsserver.so mkdir -p $(shell dirname $@) - $(CC) $(C_FLAGS) -o $@ $(LDS) obj/$*.o + $(CC) $(C_FLAGS) -o $@ $(LDS) $< obj/mds-base.o + +bin/mds: obj/mds.o bin/libmdsserver.so + mkdir -p $(shell dirname $@) + $(CC) $(C_FLAGS) -o $@ $(LDS) $< obj/mds-server/%.o: src/mds-server/%.c src/mds-server/*.h src/libmdsserver/*.h mkdir -p $(shell dirname $@) diff --git a/src/libmdsserver/util.c b/src/libmdsserver/util.c index 41c6ff1..df11b1a 100644 --- a/src/libmdsserver/util.c +++ b/src/libmdsserver/util.c @@ -49,9 +49,9 @@ char* getenv_nonempty(const char* var) * Re-exec the server. * This function only returns on failure. * - * @param argc The number of elements in `argv` - * @param argv The command line arguments - * @param reexeced Whether the server has previously been re-exec:ed + * @param argc The number of elements in `argv` + * @param argv The command line arguments + * @param reexeced Whether the server has previously been re-exec:ed */ void reexec_server(int argc, char** argv, int reexeced) { diff --git a/src/mds-base.c b/src/mds-base.c index 41f66b4..12eac19 100644 --- a/src/mds-base.c +++ b/src/mds-base.c @@ -56,7 +56,7 @@ int socket_fd = -1; * * @return Non-zero on error */ -static int parse_cmdline(void) +int __attribute__((weak)) parse_cmdline(void) { int i; @@ -90,10 +90,12 @@ static int parse_cmdline(void) } /* Check that manditory arguments have been specified. */ - exit_if (is_respawn < 0, - eprintf("missing state argument, require either %s or %s.", - "--initial-spawn", "--respawn");); - + if (server_characteristics.require_respawn_info) + { + exit_if (is_respawn < 0, + eprintf("missing state argument, require either %s or %s.", + "--initial-spawn", "--respawn");); + } return 0; } @@ -103,7 +105,7 @@ static int parse_cmdline(void) * * @return Non-zero on error */ -static int connect_to_display(void) +int __attribute__((weak)) connect_to_display(void) { char* display; char pathname[PATH_MAX]; @@ -131,6 +133,44 @@ static int connect_to_display(void) /** + * This function is called when a signal that + * signals the server to re-exec has been received + * + * When this function is invoked, it should set `reexecing` to a non-zero value + * + * @param signo The signal that has been received + */ +void __attribute__((weak)) received_reexec(int signo) +{ + (void) signo; + if (reexecing == 0) + { + reexecing = 1; + eprint("re-exec signal received."); + } +} + + +/** + * This function is called when a signal that + * signals the server to re-exec has been received + * + * When this function is invoked, it should set `terminating` to a non-zero value + * + * @param signo The signal that has been received + */ +void __attribute__((weak)) received_terminate(int signo) +{ + (void) signo; + if (terminating == 0) + { + terminating = 1; + eprint("terminate signal received."); + } +} + + +/** * Unmarshal the server's saved state * * @return Non-zero on error @@ -360,6 +400,7 @@ int trap_signals(void) return 0; pfail: + perror(*argv); return 1; } diff --git a/src/mds-base.h b/src/mds-base.h index 5aa9463..e06c348 100644 --- a/src/mds-base.h +++ b/src/mds-base.h @@ -35,12 +35,24 @@ typedef struct server_characteristics /** * Setting this to zero will cause the server to drop privileges as a security precaution */ - int require_privileges : 1; + unsigned require_privileges : 1; /** * Setting this to non-zero will cause the server to connect to the display */ - int require_display : 1; + unsigned require_display : 1; + + /** + * Setting this to non-zero will cause the server to refuse to + * start unless either --initial-spawn or --respawn is used + */ + unsigned require_respawn_info : 1; + + /** + * Setting this to non-zero will cause the server to refuse to + * start if there are too many command line arguments + */ + unsigned sanity_check_argc : 1; } __attribute__((packed)) server_characteristics_t; @@ -112,7 +124,24 @@ int trap_signals(void); /** + * Parse command line arguments + * + * @return Non-zero on error + */ +int parse_cmdline(void); /* __attribute__((weak)) */ + + +/** + * Connect to the display + * + * @return Non-zero on error + */ +int connect_to_display(void); /* __attribute__((weak)) */ + + +/** * This function should be implemented by the actual server implementation + * if the server is multithreaded * * This function is called when a signal that * signals the server to re-exec has been received @@ -121,10 +150,11 @@ int trap_signals(void); * * @param signo The signal that has been received */ -extern void received_reexec(int signo); +void received_reexec(int signo); /* __attribute__((weak)) */ /** * This function should be implemented by the actual server implementation + * if the server is multithreaded * * This function is called when a signal that * signals the server to re-exec has been received @@ -133,7 +163,7 @@ extern void received_reexec(int signo); * * @param signo The signal that has been received */ -extern void received_terminate(int signo); +void received_terminate(int signo); /* __attribute__((weak)) */ /** * This function should be implemented by the actual server implementation @@ -158,7 +188,7 @@ extern int initialise_server(void); /** * This function should be implemented by the actual server implementation * - * This function will be invoked asgter `initialise_server` (if not re-exec:ing) + * 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 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; +} + diff --git a/src/mds-respawn.h b/src/mds-respawn.h new file mode 100644 index 0000000..8c25a4a --- /dev/null +++ b/src/mds-respawn.h @@ -0,0 +1,75 @@ +/** + * 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/>. + */ +#ifndef MDS_MDS_RESPAWN_H +#define MDS_MDS_RESPAWN_H + + +#include "mds-base.h" + +#include <sys/types.h> + + + +/** + * The server has not started yet + */ +#define UNBORN 0 + +/** + * The server is up and running + */ +#define ALIVE 1 + +/** + * The server has crashed and will be respawn momentarily + */ +#define DEAD 2 + +/** + * The server crashed too fast, it will only respawn if SIGUSR2 is received + */ +#define DEAD_AND_BURIED 3 + +/** + * The server has exited successfully, it will never be respawn again + */ +#define CREMATED 4 + + + +/** + * The state and identifier of a server + */ +typedef struct server_state +{ + /** + * The server's process ID + */ + pid_t pid; + + /** + * The server's state + */ + int state; + +} server_state_t; + + + +#endif + diff --git a/src/mds-server/mds-server.c b/src/mds-server/mds-server.c index 361c52a..d6846ec 100644 --- a/src/mds-server/mds-server.c +++ b/src/mds-server/mds-server.c @@ -54,7 +54,9 @@ server_characteristics_t server_characteristics = { .require_privileges = 0, - .require_display = 0 /* We will service one ourself. */ + .require_display = 0, /* We will service one ourself. */ + .require_respawn_info = 1, + .sanity_check_argc = 1 }; @@ -167,7 +169,7 @@ int initialise_server(void) /** - * This function will be invoked asgter `initialise_server` (if not re-exec:ing) + * 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 |