aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--src/libmdsserver/util.c6
-rw-r--r--src/mds-base.c53
-rw-r--r--src/mds-base.h40
-rw-r--r--src/mds-respawn.c279
-rw-r--r--src/mds-respawn.h75
-rw-r--r--src/mds-server/mds-server.c6
7 files changed, 450 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index e24f8af..ec70381 100644
--- a/Makefile
+++ b/Makefile
@@ -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