commit badb8db957aed6bd24adc2bfe9a68d09be953385
parent faed4681b605a17046d13d3cbc33aa52514bcb0e
Author: Friedel Schön <[email protected]>
Date: Sun, 25 Jun 2023 14:01:05 +0200
using boot dummy-service instead of up-<runlevel>
Diffstat:
19 files changed, 302 insertions(+), 397 deletions(-)
diff --git a/Makefile b/Makefile
@@ -55,7 +55,7 @@ all: compile_flags.txt binary manual documentation
# Clean target
clean:
- @echo "[RM] $(TARGET_DIRS)"
+ @echo "[ RM ] $(TARGET_DIRS)"
$(SILENT)rm -rf $(TARGET_DIRS)
binary: $(BIN_FILES)
diff --git a/bin/finit.c b/bin/finit.c
@@ -63,6 +63,8 @@ int main(int argc, const char** argv) {
printf("booting...\n");
+ daemon_running = true;
+
// stage 1
handle_stage(0);
@@ -77,7 +79,7 @@ int main(int argc, const char** argv) {
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
- service_supervise(SV_SERVICE_DIR, SV_RUNLEVEL_DEFAULT);
+ service_supervise(SV_SERVICE_DIR, SV_BOOT_SERVICE, false);
sigblock_all(false);
}
diff --git a/bin/fsvc.c b/bin/fsvc.c
@@ -1,4 +1,4 @@
-// +objects: message.o util.o decode.o signame.o
+// +objects: message.o util.o signame.o
#include "config.h"
#include "message.h"
@@ -6,6 +6,7 @@
#include "signame.h"
#include "util.h"
+#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
@@ -15,42 +16,32 @@
#include <unistd.h>
-struct ident {
- const char* name;
- const char* command;
- bool runit;
-};
-
-static struct ident command_names[] = {
- { "up", "u", true }, // starts the services, pin as started
- { "down", "d", true }, // stops the service, pin as stopped
- { "xup", "U", true }, // stops the service, don't pin as stopped
- { "xdown", "D", true }, // stops the service, don't pin as stopped
- { "once", "o", true }, // same as xup
- { "term", "t", true }, // same as down
- { "kill", "k", true }, // sends kill, pin as stopped
- { "pause", "p", true }, // pauses the service
- { "cont", "c", true }, // resumes the service
- { "reset", "r", true }, // resets the service
- { "alarm", "a", true }, // sends alarm
- { "hup", "h", true }, // sends hup
- { "int", "i", true }, // sends interrupt
- { "quit", "q", true }, // sends quit
- { "1", "1", true }, // sends usr1
- { "2", "2", true }, // sends usr2
- { "exit", "x", true }, // does nothing
- { "+up", "U0", false }, // starts the service, don't modify pin
- { "+down", "D0", false }, // stops the service, don't modify pin
- { "restart", "!D0U0", false }, // restarts the service, don't modify pin
- { "start", "!u", true }, // start the service, pin as started, print status
- { "stop", "!d", true }, // stop the service, pin as stopped, print status
- { "status", "!", true }, // print status
- { "check", "!", true }, // print status
- { "enable", "!.e", false }, // enable service
- { "disable", "!.d", false }, // disable service
- { "enable-once", "!.e", false }, // enable service once
- { "disable-once", "!.d", false }, // disable service once
- { 0, 0, 0 }
+static const char* const command_names[][2] = {
+ { "up", "u" }, // starts the services, pin as started
+ { "down", "d" }, // stops the service, pin as stopped
+ { "once", "o" }, // same as xup
+ { "term", "t" }, // same as down
+ { "kill", "k" }, // sends kill, pin as stopped
+ { "pause", "p" }, // pauses the service
+ { "cont", "c" }, // resumes the service
+ { "reset", "r" }, // resets the service
+ { "alarm", "a" }, // sends alarm
+ { "hup", "h" }, // sends hup
+ { "int", "i" }, // sends interrupt
+ { "quit", "q" }, // sends quit
+ { "1", "1" }, // sends usr1
+ { "2", "2" }, // sends usr2
+ { "usr1", "1" }, // sends usr1
+ { "usr2", "2" }, // sends usr2
+ { "exit", "x" }, // does nothing
+ { "restart", "!du" }, // restarts the service, don't modify pin
+ { "start", "!u" }, // start the service, pin as started, print status
+ { "stop", "!d" }, // stop the service, pin as stopped, print status
+ { "status", "!" }, // print status
+ { "check", "!" }, // print status
+ { "enable", "!.e" }, // enable service
+ { "disable", "!.d" }, // disable service
+ { 0, 0 }
};
static const struct option long_options[] = {
@@ -59,25 +50,50 @@ static const struct option long_options[] = {
{ 0 }
};
-static int check_service(int dir, char* runlevel) {
- int fd;
- ssize_t size;
-
- if ((fd = openat(dir, "supervise/ok", O_WRONLY | O_NONBLOCK)) == -1)
- return -1;
- close(fd);
-
- if ((fd = openat(dir, "supervise/runlevel", O_RDONLY)) == -1)
- return -1;
-
- if ((size = read(fd, runlevel, NAME_MAX)) == -1) {
- close(fd);
- return -1;
- }
- runlevel[size] = '\0';
- close(fd);
+struct service_decode {
+ int state;
+ pid_t pid;
+ time_t state_change;
+ bool restart;
+ bool once;
+ bool is_depends;
+ bool wants_up;
+ int last_exit;
+ int return_code;
+ uint8_t fail_count;
+ bool paused;
+ bool is_terminating;
+};
- return 0;
+static void decode(struct service_decode* s, const struct service_serial* buffer) {
+ uint64_t tai = ((uint64_t) buffer->status_change[0] << 56) |
+ ((uint64_t) buffer->status_change[1] << 48) |
+ ((uint64_t) buffer->status_change[2] << 40) |
+ ((uint64_t) buffer->status_change[3] << 32) |
+ ((uint64_t) buffer->status_change[4] << 24) |
+ ((uint64_t) buffer->status_change[5] << 16) |
+ ((uint64_t) buffer->status_change[6] << 8) |
+ ((uint64_t) buffer->status_change[7] << 0);
+
+ s->state_change = tai - 4611686018427387914ULL;
+
+ s->state = buffer->state;
+ s->return_code = buffer->return_code;
+ s->fail_count = buffer->fail_count;
+ s->is_terminating = (buffer->flags >> 4) & 0x01;
+ s->once = (buffer->flags >> 3) & 0x01;
+ s->restart = (buffer->flags >> 2) & 0x01;
+ s->last_exit = (buffer->flags >> 0) & 0x03;
+
+ s->pid = (buffer->pid[0] << 0) |
+ (buffer->pid[1] << 8) |
+ (buffer->pid[2] << 16) |
+ (buffer->pid[3] << 24);
+
+ s->paused = buffer->paused;
+ s->wants_up = buffer->restart == 'u';
+
+ s->is_depends = s->wants_up != (s->once || s->restart);
}
static time_t get_mtime(int dir) {
@@ -87,37 +103,15 @@ static time_t get_mtime(int dir) {
return st.st_mtim.tv_sec;
}
-static int handle_command(int dir, char command, const char* runlevel) {
- int fd;
-
- char up_file[SV_NAME_MAX] = "up-";
- char once_file[SV_NAME_MAX] = "once-";
+static int handle_command(int dir, char command) {
+ // no custom commands defined
- strcat(up_file, runlevel);
- strcat(once_file, runlevel);
+ (void) dir, (void) command;
- switch (command) {
- case 'e': // enable
- if ((fd = openat(dir, up_file, O_WRONLY | O_TRUNC | O_CREAT, 0644)) != -1)
- close(fd);
- break;
- case 'd':
- unlinkat(dir, up_file, 0);
- break;
- case 'E': // enable
- if ((fd = openat(dir, once_file, O_WRONLY | O_TRUNC | O_CREAT, 0644)) != -1)
- close(fd);
- break;
- case 'D':
- unlinkat(dir, once_file, 0);
- break;
- default:
- return -1;
- }
- return 0;
+ return -1;
}
-static int send_command(int dir, const char* command, const char* runlevel) {
+static int send_command(int dir, const char* command) {
int fd;
if ((fd = openat(dir, "supervise/control", O_WRONLY | O_NONBLOCK)) == -1)
return -1;
@@ -125,7 +119,7 @@ static int send_command(int dir, const char* command, const char* runlevel) {
for (const char* c = command; *c != '\0'; c++) {
if (*c == '.') {
c++;
- if (handle_command(dir, *c, runlevel) == -1)
+ if (handle_command(dir, *c) == -1)
return -1;
} else {
if (write(fd, c, 1) == -1)
@@ -142,7 +136,7 @@ int status(int dir) {
time_t timeval;
const char* timeunit = "sec";
struct service_serial buffer;
- service_t s;
+ struct service_decode s;
if ((fd = openat(dir, "supervise/status", O_RDONLY | O_NONBLOCK)) == -1)
return -1;
@@ -154,9 +148,9 @@ int status(int dir) {
close(fd);
- service_decode(&s, &buffer);
+ decode(&s, &buffer);
- timeval = time(NULL) - s.status_change;
+ timeval = time(NULL) - s.state_change;
if (timeval >= 60) {
timeval /= 60;
@@ -194,8 +188,8 @@ int status(int dir) {
case STATE_INACTIVE:
printf("inactive");
break;
- case STATE_DEAD:
- printf("dead");
+ case STATE_ERROR:
+ printf("dead (error)");
break;
}
@@ -204,26 +198,20 @@ int status(int dir) {
printf(" since %lu%s", timeval, timeunit);
- if (s.restart_manual == S_ONCE && s.restart_file != S_ONCE)
- printf(", manually started once");
- else if (s.restart_manual == S_RESTART && s.restart_file != S_RESTART)
- printf(", manually restart");
- else if (s.restart_manual == S_FORCE_DOWN && s.restart_file != S_DOWN)
- printf(", manually forced down");
+ if (s.once == S_ONCE)
+ printf(", started once");
- if (s.restart_file == S_ONCE)
- printf(", should started once");
- else if (s.restart_file == S_RESTART)
+ if (s.restart)
printf(", should restart");
- if (s.is_dependency)
+ if (s.is_depends)
printf(", started as dependency");
if (s.return_code > 0 && s.last_exit == EXIT_NORMAL)
printf(", exited with %d", s.return_code);
if (s.return_code > 0 && s.last_exit == EXIT_SIGNALED)
- printf(", signaled with SIG%s", sigabbr(s.return_code));
+ printf(", crashed with SIG%s", sigabbr(s.return_code));
if (s.fail_count > 0)
printf(", failed %d times", s.fail_count);
@@ -234,12 +222,12 @@ int status(int dir) {
}
int main(int argc, char** argv) {
- int opt, dir,
+ int opt, dir, fd,
timeout = SV_STATUS_WAIT;
time_t mod, start;
const char* command = NULL;
- char runlevel[SV_NAME_MAX];
+ const char* service;
while ((opt = getopt_long(argc, argv, ":Vw:", long_options, NULL)) != -1) {
switch (opt) {
@@ -265,9 +253,9 @@ int main(int argc, char** argv) {
fprintf(stderr, "error: command omitted\n");
print_usage_exit(PROG_FSVC, 1);
}
- for (struct ident* ident = command_names; ident->name != NULL; ident++) {
- if (streq(ident->name, argv[0])) {
- command = ident->command;
+ for (const char** ident = (void*) command_names; ident[0] != NULL; ident++) {
+ if (streq(ident[0], argv[0])) {
+ command = ident[1];
break;
}
}
@@ -291,25 +279,27 @@ int main(int argc, char** argv) {
}
for (int i = 0; i < argc; i++) {
+ service = progname(argv[i]);
+
if ((dir = open(argv[i], O_DIRECTORY)) == -1) {
- fprintf(stderr, "warning: '%s' is not a valid directory\n", argv[i]);
+ fprintf(stderr, "error: %s: cannot open directory: %s\n", argv[i], strerror(errno));
continue;
}
-
- if (check_service(dir, runlevel) == -1) {
- fprintf(stderr, "warning: '%s' is not a valid service\n", argv[i]);
+ if ((fd = openat(dir, "supervise/ok", O_WRONLY | O_NONBLOCK)) == -1) {
+ fprintf(stderr, "error: %s: cannot open supervise/control: %s\n", argv[i], strerror(errno));
continue;
}
+ close(fd);
if ((mod = get_mtime(dir)) == -1) {
- fprintf(stderr, "warning: cannot get modify-time of '%s'\n", argv[i]);
+ fprintf(stderr, "error: %s: cannot get modify-time\n", argv[i]);
continue;
}
if (command[0] != '\0') {
- if (send_command(dir, command, runlevel) == -1) {
- fprintf(stderr, "warning: unable to send command to '%s'\n", argv[i]);
+ if (send_command(dir, command) == -1) {
+ fprintf(stderr, "error: %s: unable to send command\n", argv[i]);
continue;
}
} else {
@@ -324,7 +314,7 @@ int main(int argc, char** argv) {
if (get_mtime(dir) == mod)
printf("timeout: ");
- printf("%s: ", progname(argv[i]));
+ printf("%s: ", service);
if (status(dir) == -1)
printf("unable to access supervise/status\n");
diff --git a/bin/fsvs.c b/bin/fsvs.c
@@ -15,6 +15,7 @@
static const struct option long_options[] = {
{ "version", no_argument, 0, 'V' },
+ { "once", no_argument, 0, 'o' },
{ 0 }
};
@@ -25,11 +26,16 @@ static void signal_interrupt(int signum) {
}
int main(int argc, char** argv) {
- int c;
- while ((c = getopt_long(argc, argv, ":V", long_options, NULL)) > 0) {
+ int c;
+ bool once = false;
+ while ((c = getopt_long(argc, argv, ":Vo", long_options, NULL)) > 0) {
switch (c) {
case 'V':
print_version_exit();
+ break;
+ case 'o':
+ once = true;
+ break;
default:
case '?':
if (optopt)
@@ -58,5 +64,5 @@ int main(int argc, char** argv) {
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
- return service_supervise(argv[0], argv[1]);
+ return service_supervise(argv[0], argv[1], once);
}
diff --git a/configure b/configure
@@ -28,7 +28,7 @@ H_STOP_TIMEOUT=5 # seconds
H_NAME_MAX=128 # chars
H_FAIL_MAX=32 # times
H_SERVICE_MAX=128 # services
-H_RUNLEVEL_DEFAULT="default"
+H_BOOT_SERVICE="boot"
H_CHECK_INTERVAL=3 # seconds
H_PARAM_FILE_LINE_MAX=1024 # bytes
H_ENV_FILE_LINE_MAX=1024 # bytes
@@ -171,7 +171,7 @@ while [ -n "$1" ]; do
--fail-limit) H_FAIL_MAX=$2; shift 2;;
--service-name-limit) H_NAME_MAX=$2; shift 2;;
--service-limit) H_SERVICE_MAX=$2; shift 2;;
- --default-runlevel) H_RUNLEVEL_DEFAULT=$2; shift 2;;
+ --boot-service) H_BOOT_SERVICE=$2; shift 2;;
--check-interval) H_CHECK_INTERVAL=$2; shift 2;;
--rescure-shell) H_RESCUE_SHELL=$2; shift 2;;
--total-depends-limit) H_DEPENDENCY_MAX=$2; shift 2;;
@@ -244,7 +244,7 @@ cat > config.h <<EOF
#define SV_USER_GROUP_MAX $H_USER_GROUP_MAX
#define SV_VLOGGER_BUFFER $H_VLOGGER_BUFFER
#define SV_STATUS_WAIT $H_STATUS_WAIT
-#define SV_RUNLEVEL_DEFAULT "$H_RUNLEVEL_DEFAULT"
+#define SV_BOOT_SERVICE "$H_BOOT_SERVICE"
#define SV_LOG_DIR "$H_LOG_DIR"
#define SV_RESCUE_SHELL "$H_RESCUE_SHELL"
#define SV_SERVICE_DIR "$H_SERVICE_DIR"
diff --git a/contrib/serialize.txt b/contrib/serialize.txt
@@ -2,9 +2,9 @@ FISS CONTROL RESPONSE
=====================
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-FISS: | STATUS CHANGE |ST|RC|FC|FL| PID |PS|RS|FD|SR|
+FISS: | STATUS CHANGE |ST|RC|FC|FL| PID |PS|WU|--|SR|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
-RUNIT: | STATUS CHANGE | (NANOSEC) | PID |PS|WU|TR|SR|
+RUNIT: | STATUS CHANGE | NANOSEC | PID |PS|WU|TR|SR|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
STATUS CHANGE = unix seconds + 4611686018427387914ULL (tai, lower endian)
@@ -14,26 +14,26 @@ FC = fail count
FL = flags (see below)
PID = current pid (big endian)
PS = is paused (int boolean)
-RS = restart ('u' if want up, 'd' if want down)
-FD = forced down (int boolean, unused by fiss)
+WU = wants up ('u' if want up, 'd' if want down)
SR = state runit (0 is down, 1 is running, 2 is finishing, not available in daemontools, unused by fiss)
NANOSEC = unix nanoseconds (written, never read)
-WU = wants up ('u' if want up, 'd' if want down)
TR = was terminated (int boolean)
FLAGS
-----
+--+--+--+--+--+--+--+--+
-|--|ID|RSFIL|RSMAN|LSTEX|
+| -- |TR|OC|RS|LSTEX|
+--+--+--+--+--+--+--+--+
-ID = is dependency
-RSFIL = restart file (0 = down, 2 = once, 3 = restart)
-RSMAN = restart manual override (0 = down, 1 = force down, 2 = once, 3 = restart)
+TR = is terminating
+OC = started once
+RS = should restart
LSTEX = last exit (0 = never exitted, 1 = normally, 2 = signaled)
-- = nothing yet
+; as dependency : wants-up != (restart || once)
+
STATE
-----
diff --git a/include/service.h b/include/service.h
@@ -11,8 +11,6 @@
enum service_command {
X_UP = 'u', // starts the services, pin as started
X_DOWN = 'd', // stops the service, pin as stopped
- X_XUP = 'U', // starts the service, set restart (d = down, f = force_down, o = once, u = up)
- X_XDOWN = 'D', // stops the service, set restart (d = down, f = force_down, o = once, u = up)
X_ONCE = 'o', // starts the service, pin as once
X_TERM = 't', // same as down
X_KILL = 'k', // sends kill, pin as stopped
@@ -29,28 +27,27 @@ enum service_command {
};
enum service_state {
- STATE_INACTIVE,
- STATE_SETUP,
- STATE_STARTING,
- STATE_ACTIVE_FOREGROUND,
- STATE_ACTIVE_BACKGROUND,
- STATE_ACTIVE_DUMMY,
- STATE_STOPPING,
- STATE_FINISHING,
- STATE_DEAD,
+ STATE_INACTIVE, // not started
+ STATE_SETUP, // ./setup running
+ STATE_STARTING, // ./start running
+ STATE_ACTIVE_FOREGROUND, // ./run running
+ STATE_ACTIVE_BACKGROUND, // ./start finished, ./stop not called yet
+ STATE_ACTIVE_DUMMY, // dependencies started
+ STATE_STOPPING, // ./stop running
+ STATE_FINISHING, // ./finish running
+ STATE_ERROR, // something went wrong
};
enum service_exit {
- EXIT_NONE,
- EXIT_NORMAL,
- EXIT_SIGNALED,
+ EXIT_NONE, // never exited
+ EXIT_NORMAL, // exited
+ EXIT_SIGNALED, // crashed
};
enum service_restart {
- S_DOWN,
- S_FORCE_DOWN, // force down (manual)
- S_ONCE,
- S_RESTART,
+ S_DOWN, // service should not be started
+ S_ONCE, // service should
+ S_RESTART, // service should be started
};
struct service_serial {
@@ -66,29 +63,28 @@ struct service_serial {
uint8_t state_runit;
};
-typedef struct service {
- char name[SV_NAME_MAX]; // name of service
- int dir; // dirfd
- int state;
- int control; // fd to supervise/control
- pid_t pid; // pid of run
- time_t status_change; // last status change
- pipe_t log_pipe; // pipe for logging
- int restart_manual; // should restart on exit
- int restart_file; // should restart on exit
- bool restart_final; // combined restart + dependency (only set client-side)
- int last_exit; // stopped signaled or exited
- int return_code; // return code or signal
- uint8_t fail_count; // current fail cound
- bool is_log_service; // is a log service
- bool paused; // is paused
- struct service* log_service; // has a log_server otherwise NULL
- bool is_dependency; // only set by service_load
-} service_t;
+typedef struct service service_t;
+
+struct service {
+ char name[SV_NAME_MAX]; // name of service
+ int state; // current state
+ pid_t pid; // pid of run
+ int dir; // dirfd
+ int control; // fd to supervise/control
+ time_t state_change; // last status change
+ int restart; // should restart on exit
+ int last_exit; // stopped signaled or exited
+ int return_code; // return code or signal
+ uint8_t fail_count; // current fail cound
+ bool is_log_service; // is a log service
+ bool paused; // is paused
+ time_t stop_timeout; // stop start-time
+ pipe_t log_pipe; // pipe for logging
+ service_t* log_service; // has a log_server otherwise NULL
+};
extern service_t services[];
extern int services_size;
-extern char runlevel[];
extern int service_dir;
extern int null_fd;
extern bool daemon_running;
@@ -97,11 +93,9 @@ extern int depends_size;
extern const char* service_dir_path;
-void service_decode(service_t* s, const void* buffer); // for fsvc
-void service_encode(service_t* s, void* buffer); // for fsvs
+void service_encode(service_t* s, void* buffer);
service_t* service_get(const char* name);
-void service_handle_client(int client);
-void service_handle_command(service_t* s, char command, char data);
+void service_handle_command(service_t* s, char command);
void service_handle_exit(service_t* s, bool signaled, int return_code);
void service_kill(service_t* s, int signal);
bool service_need_restart(service_t* s);
@@ -113,7 +107,7 @@ int service_send_command(char command, char extra, const char* service,
void service_start(service_t* s);
const char* service_status_name(service_t* s);
void service_stop(service_t* s);
-int service_supervise(const char* service_dir, const char* runlevel);
+int service_supervise(const char* service_dir_, const char* service, bool once);
void service_update_dependency(service_t* s);
bool service_is_dependency(service_t* s);
void service_update_state(service_t* s, int state);
diff --git a/include/util.h b/include/util.h
@@ -2,9 +2,7 @@
#include <stdio.h>
-#define streq(a, b) (!strcmp((a), (b)))
-#define stringify(s) #s
-#define static_stringify(s) stringify(s)
+#define streq(a, b) (!strcmp((a), (b)))
#define print_error(msg, ...) (fprintf(stderr, msg, ##__VA_ARGS__, strerror(errno)))
diff --git a/mk/target.mk b/mk/target.mk
@@ -1,17 +1,17 @@
# Directory rules
$(TARGET_DIRS):
- @echo "[MKDIR] $@"
+ @echo "[MDIR] $@"
$(SILENT)mkdir -p $@
$(TARGET_ASSETS_DIR): $(ASSETS_DIR) | $(TARGET_DOCS_DIR)
- @echo "[CP] $@"
+ @echo "[COPY] $@"
$(SILENT)mkdir -p $@
$(SILENT)cp -r $</* $@
# Object rules
$(TARGET_OBJECT_DIR)/%.o: $(SRC_DIR)/%.c $(INCLUDE_FILES) | $(TARGET_OBJECT_DIR)
- @echo "[CC] $@"
+ @echo "[ CC ] $@"
$(SILENT)$(CC) -o $@ $< -c $(CFLAGS) $(shell mk/extract-flags.sh $< $(TARGET_OBJECT_DIR))
# Executables
@@ -20,25 +20,25 @@ $(TARGET_BIN_DIR)/%: $(BIN_DIR)/%.c $(OBJ_FILES) | $(TARGET_BIN_DIR)
$(SILENT)$(CC) -o $@ $< $(CFLAGS) $(shell mk/extract-flags.sh $< $(TARGET_OBJECT_DIR)) $(LDFLAGS)
$(TARGET_BIN_DIR)/%: $(BIN_DIR)/%.sh | $(TARGET_BIN_DIR)
- @echo "[CP] $@"
+ @echo "[COPY] $@"
$(SILENT)cp $< $@
$(SILENT)chmod +x $@
$(TARGET_BIN_DIR)/%: $(BIN_DIR)/%.lnk | $(TARGET_BIN_DIR)
- @echo "[LN] $@"
+ @echo "[LINK] $@"
$(SILENT)ln -sf $(shell cat $<) $@
# Documentation and Manual
$(TARGET_DOCS_DIR)/%.html: $(DOCS_DIR)/%.txt $(TARGET_ASSETS_DIR) $(MAKE_DOCS) | $(TARGET_DOCS_DIR)
- @echo "[MKDOC] $@"
+ @echo "[MDOC] $@"
$(SILENT)$(SED) 's/%VERSION%/$(VERSION)/' $< | $(PYTHON) $(MAKE_DOCS) > $@
$(TARGET_DOCS_DIR)/%.html: $(MAN_DIR)/%.txt $(TARGET_ASSETS_DIR) $(MAKE_DOCS) | $(TARGET_DOCS_DIR)
- @echo "[MKDOC] $@"
+ @echo "[MDOC] $@"
$(SILENT)$(SED) 's/%VERSION%/$(VERSION)/' $< | $(PYTHON) $(MAKE_DOCS) > $@
$(TARGET_MAN_DIR)/%: $(MAN_DIR)/%.txt $(MAKE_MAN) | $(TARGET_MAN_DIR)
- @echo "[MKMAN] $@"
+ @echo "[MMAN] $@"
$(SILENT)$(SED) 's/%VERSION%/$(VERSION)/' $< | $(PYTHON) $(MAKE_MAN) | $(AWK) '/./ { print }' > $@
# Debug
diff --git a/src/decode.c b/src/decode.c
@@ -1,33 +0,0 @@
-#include "service.h"
-
-
-void service_decode(service_t* s, const void* buffer_ptr) {
- const struct service_serial* buffer = buffer_ptr;
-
- uint64_t tai = ((uint64_t) buffer->status_change[0] << 56) |
- ((uint64_t) buffer->status_change[1] << 48) |
- ((uint64_t) buffer->status_change[2] << 40) |
- ((uint64_t) buffer->status_change[3] << 32) |
- ((uint64_t) buffer->status_change[4] << 24) |
- ((uint64_t) buffer->status_change[5] << 16) |
- ((uint64_t) buffer->status_change[6] << 8) |
- ((uint64_t) buffer->status_change[7]);
-
- s->status_change = tai - 4611686018427387914ULL;
-
- s->state = buffer->state;
- s->return_code = buffer->return_code;
- s->fail_count = buffer->fail_count;
- s->is_dependency = (buffer->flags >> 6) & 0x01;
- s->restart_file = (buffer->flags >> 4) & 0x03;
- s->restart_manual = (buffer->flags >> 2) & 0x03;
- s->last_exit = (buffer->flags >> 0) & 0x03;
-
- s->pid = (buffer->pid[0] << 0) |
- (buffer->pid[1] << 8) |
- (buffer->pid[2] << 16) |
- (buffer->pid[3] << 24);
-
- s->paused = buffer->paused;
- s->restart_final = buffer->restart == 'u';
-}
diff --git a/src/encode.c b/src/encode.c
@@ -4,12 +4,12 @@
void service_encode(service_t* s, void* buffer_ptr) {
struct service_serial* buffer = buffer_ptr;
- uint64_t tai = (uint64_t) s->status_change + 4611686018427387914ULL;
+ uint64_t tai = (uint64_t) s->state_change + 4611686018427387914ULL;
int state_runit;
switch (s->state) {
case STATE_INACTIVE:
- case STATE_DEAD:
+ case STATE_ERROR:
state_runit = 0;
break;
case STATE_SETUP:
@@ -38,9 +38,9 @@ void service_encode(service_t* s, void* buffer_ptr) {
buffer->return_code = s->return_code;
buffer->fail_count = s->fail_count;
- buffer->flags = (service_is_dependency(s) << 6) |
- (s->restart_file << 4) |
- (s->restart_manual << 2) |
+ buffer->flags = ((s->stop_timeout != 0) << 4) |
+ ((s->restart == S_ONCE) << 3) |
+ ((s->restart == S_RESTART) << 2) |
(s->last_exit << 0);
buffer->pid[0] = (s->pid >> 0) & 0xff;
@@ -50,6 +50,6 @@ void service_encode(service_t* s, void* buffer_ptr) {
buffer->paused = s->paused;
buffer->restart = service_need_restart(s) ? 'u' : 'd';
- buffer->force_down = s->restart_manual == S_FORCE_DOWN;
+ buffer->force_down = 0;
buffer->state_runit = state_runit;
}
diff --git a/src/handle_command.c b/src/handle_command.c
@@ -17,57 +17,23 @@ static int runit_signals[] = {
[X_USR2] = SIGUSR2,
};
-void service_handle_command(service_t* s, char command, char data) {
- switch (command) {
+void service_handle_command(service_t* s, char command) {
+ switch ((enum service_command) command) {
case X_UP:
- s->restart_manual = S_RESTART;
+ s->restart = S_RESTART;
service_start(s);
break;
case X_ONCE:
- s->restart_manual = S_ONCE;
+ s->restart = S_ONCE;
service_start(s);
break;
case X_DOWN:
case X_TERM:
- s->restart_manual = S_FORCE_DOWN;
- service_stop(s);
- break;
- case X_XUP:
- switch (data) {
- case 'd':
- s->restart_manual = S_DOWN;
- break;
- case 'f':
- s->restart_manual = S_FORCE_DOWN;
- break;
- case 'o':
- s->restart_manual = S_ONCE;
- break;
- case 'u':
- s->restart_manual = S_RESTART;
- break;
- }
- service_start(s);
- break;
- case X_XDOWN:
- switch (data) {
- case 'd':
- s->restart_manual = S_DOWN;
- break;
- case 'f':
- s->restart_manual = S_FORCE_DOWN;
- break;
- case 'o':
- s->restart_manual = S_ONCE;
- break;
- case 'u':
- s->restart_manual = S_RESTART;
- break;
- }
+ s->restart = S_DOWN;
service_stop(s);
break;
case X_KILL:
- s->restart_manual = S_FORCE_DOWN;
+ s->restart = S_DOWN;
service_kill(s, SIGKILL);
break;
case X_PAUSE:
@@ -97,7 +63,7 @@ void service_handle_command(service_t* s, char command, char data) {
}
s->fail_count = 0;
- if (s->state == STATE_DEAD)
+ if (s->state == STATE_ERROR)
service_update_state(s, STATE_INACTIVE);
break;
diff --git a/src/handle_exit.c b/src/handle_exit.c
@@ -21,7 +21,7 @@ static void do_finish(service_t* s) {
service_update_state(s, STATE_FINISHING);
}
} else if (s->fail_count == SV_FAIL_MAX) {
- service_update_state(s, STATE_DEAD);
+ service_update_state(s, STATE_ERROR);
printf("%s died\n", s->name);
} else {
service_update_state(s, STATE_INACTIVE);
@@ -32,11 +32,11 @@ static void do_finish(service_t* s) {
void service_handle_exit(service_t* s, bool signaled, int return_code) {
struct stat st;
- s->pid = 0;
- if (s->restart_file == S_ONCE)
- s->restart_file = S_DOWN;
- if (s->restart_manual == S_ONCE)
- s->restart_manual = S_DOWN;
+ s->pid = 0;
+ s->stop_timeout = 0;
+
+ if (s->restart == S_ONCE)
+ s->restart = S_DOWN;
switch (s->state) {
case STATE_SETUP:
@@ -71,7 +71,7 @@ void service_handle_exit(service_t* s, bool signaled, int return_code) {
case STATE_FINISHING:
if (s->fail_count == SV_FAIL_MAX) {
- service_update_state(s, STATE_DEAD);
+ service_update_state(s, STATE_ERROR);
printf("%s died\n", s->name);
} else {
service_update_state(s, STATE_INACTIVE);
@@ -97,7 +97,7 @@ void service_handle_exit(service_t* s, bool signaled, int return_code) {
}
break;
- case STATE_DEAD:
+ case STATE_ERROR:
case STATE_INACTIVE:
printf("warn: %s died but was set to inactive\n", s->name);
}
diff --git a/src/register.c b/src/register.c
@@ -15,21 +15,20 @@ static int init_supervise(service_t* s) {
int fd;
struct stat st;
- if (fstatat(s->dir, "supervise", &st, 0) == -1 && mkdirat(s->dir, "supervise", 0744) == -1) {
- print_error("warning: cannot create directory supervise: %s\ntrying to remove supervise...\n");
- if (unlinkat(s->dir, "supervise", 0) == -1 || mkdirat(s->dir, "supervise", 0744) == -1) {
- print_error("warning: cannot create directory supervise: %s\n");
- return -1;
- }
+ if (fstatat(s->dir, "supervise", &st, 0) == -1 && mkdirat(s->dir, "supervise", 0755) == -1) {
+ return -1;
}
- if (fstatat(s->dir, "supervise/ok", &st, 0) == -1 && mkfifoat(s->dir, "supervise/ok", 0644) == -1) {
+
+ if (fstatat(s->dir, "supervise/ok", &st, 0) == -1 && mkfifoat(s->dir, "supervise/ok", 0666) == -1) {
print_error("cannot create fifo at supervise/ok: %s\n");
return -1;
}
+
if (fstatat(s->dir, "supervise/control", &st, 0) == -1 && mkfifoat(s->dir, "supervise/control", 0644) == -1) {
print_error("cannot create fifo at supervise/control: %s\n");
return -1;
}
+
if (openat(s->dir, "supervise/ok", O_RDONLY | O_NONBLOCK) == -1) {
print_error("cannot open supervise/ok: %s\n");
return -1;
@@ -40,29 +39,12 @@ static int init_supervise(service_t* s) {
return -1;
}
- if (fd_set_flag(s->control, O_NONBLOCK)) {
- print_error("cannot set supervise/control non-blocking: %s\n");
- }
-
if ((fd = openat(s->dir, "supervise/lock", O_CREAT | O_WRONLY, 0644)) == -1) {
print_error("cannot create supervise/lock: %s\n");
return -1;
}
close(fd);
-
- if ((fd = openat(s->dir, "supervise/runlevel", O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
- print_error("cannot create supervise/runlevel: %s\n");
- return -1;
- }
-
- if (write(fd, runlevel, strlen(runlevel)) == -1) {
- print_error("cannot write to supervise/runlevel: %s\n");
- close(fd);
- return -1;
- }
- close(fd);
-
return 0;
}
@@ -70,15 +52,10 @@ service_t* service_register(int dir, const char* name, bool is_log_service) {
service_t* s;
struct stat st;
- char up_path[SV_NAME_MAX] = "up-",
- once_path[SV_NAME_MAX] = "once-";
-
-
if ((s = service_get(name)) == NULL) {
s = &services[services_size++];
s->state = STATE_INACTIVE;
- s->restart_manual = S_DOWN;
- s->restart_file = S_DOWN;
+ s->restart = S_DOWN;
s->last_exit = EXIT_NONE;
s->return_code = 0;
s->fail_count = 0;
@@ -87,6 +64,7 @@ service_t* service_register(int dir, const char* name, bool is_log_service) {
s->log_pipe.read = 0;
s->log_pipe.write = 0;
s->is_log_service = is_log_service;
+ s->stop_timeout = 0;
if ((s->dir = openat(dir, name, O_DIRECTORY)) == -1) {
print_error("error: cannot open '%s': %s\n", name);
@@ -114,16 +92,6 @@ service_t* service_register(int dir, const char* name, bool is_log_service) {
s->log_service = service_register(s->dir, "log", true);
}
- strcat(up_path, runlevel);
- strcat(once_path, runlevel);
-
- s->restart_file = S_DOWN;
-
- if (fstatat(s->dir, up_path, &st, 0) != -1 && st.st_mode & S_IREAD)
- s->restart_file = S_RESTART;
- else if (fstatat(s->dir, once_path, &st, 0) != -1 && st.st_mode & S_IREAD)
- s->restart_file = S_ONCE;
-
service_write(s);
return s;
diff --git a/src/service.c b/src/service.c
@@ -21,6 +21,7 @@ int control_socket;
int null_fd;
service_t* depends[SV_DEPENDENCY_MAX][2];
int depends_size;
+bool daemon_running;
service_t* service_get(const char* name) {
for (int i = 0; i < services_size; i++) {
@@ -54,15 +55,9 @@ int service_refresh_directory(void) {
for (int i = 0; i < services_size; i++) {
s = &services[i];
if (fstat(s->dir, &st) == -1 || !S_ISDIR(st.st_mode)) {
- if (s->pid)
- kill(s->pid, SIGKILL);
+ service_stop(s);
close(s->dir);
close(s->control);
- if (i < services_size - 1) {
- memmove(services + i, services + i + 1, services_size - i - 1);
- i--;
- }
- services_size--;
}
}
@@ -86,27 +81,23 @@ int service_refresh_directory(void) {
}
-bool service_is_dependency(service_t* d) {
- service_t* s;
-
- for (int i = 0; i < depends_size; i++) {
- s = depends[i][0];
- if (depends[i][1] == d && (s->state != STATE_INACTIVE || service_need_restart(s)))
- return true;
- }
- return false;
-}
-
bool service_need_restart(service_t* s) {
+ service_t* d;
+
if (!daemon_running)
return false;
- if (s->restart_manual == S_FORCE_DOWN)
- return service_is_dependency(s);
+ if (s->restart == S_RESTART)
+ return true;
- return s->restart_file == S_ONCE ||
- s->restart_file == S_RESTART ||
- s->restart_manual == S_ONCE ||
- s->restart_manual == S_RESTART ||
- service_is_dependency(s);
+ for (int i = 0; i < depends_size; i++) {
+ if (depends[i][1] != s)
+ continue;
+
+ d = depends[i][0];
+ if (service_need_restart(d))
+ return true;
+ }
+
+ return false;
}
diff --git a/src/start.c b/src/start.c
@@ -41,7 +41,7 @@ static void set_pipes(service_t* s) {
char service_log[PATH_MAX];
int log_fd;
- snprintf(service_log, PATH_MAX, "%s/%s-%s.log", SV_LOG_DIR, s->name, runlevel);
+ snprintf(service_log, PATH_MAX, "%s/%s.log", SV_LOG_DIR, s->name);
if ((log_fd = open(service_log, O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1)
log_fd = null_fd;
@@ -136,7 +136,7 @@ void service_run(service_t* s) {
void service_start(service_t* s) {
struct stat st;
- if (s->state != STATE_INACTIVE)
+ if (!daemon_running || s->state != STATE_INACTIVE)
return;
printf("starting %s\n", s->name);
diff --git a/src/status.c b/src/status.c
@@ -15,7 +15,7 @@ void service_update_state(service_t* s, int state) {
if (state != -1)
s->state = state;
- s->status_change = time(NULL);
+ s->state_change = time(NULL);
service_write(s);
}
@@ -84,8 +84,8 @@ const char* service_status_name(service_t* s) {
return "stopping";
case STATE_INACTIVE:
return "down";
- case STATE_DEAD:
- return "dead";
+ case STATE_ERROR:
+ return "dead (error)";
default:
return NULL;
}
diff --git a/src/stop.c b/src/stop.c
@@ -10,6 +10,9 @@
void service_stop(service_t* s) {
+ if (s->stop_timeout > 0)
+ return;
+
switch (s->state) {
case STATE_ACTIVE_DUMMY:
service_handle_exit(s, false, 0);
@@ -28,11 +31,11 @@ void service_stop(service_t* s) {
case STATE_STOPPING:
case STATE_FINISHING:
kill(s->pid, SIGTERM);
-
+ s->stop_timeout = time(NULL);
service_update_state(s, -1);
break;
case STATE_INACTIVE:
- case STATE_DEAD:
+ case STATE_ERROR:
break;
}
}
diff --git a/src/supervise.c b/src/supervise.c
@@ -15,8 +15,6 @@
#include <unistd.h>
-bool daemon_running = true;
-
static void signal_child(int unused) {
(void) unused;
@@ -49,54 +47,71 @@ static void update_services(void) {
for (int i = 0; i < services_size; i++) {
s = &services[i];
- if (s->state == STATE_DEAD)
+ if (s->state == STATE_ERROR)
continue;
- if (service_need_restart(s)) {
- if (s->state == STATE_INACTIVE) {
- service_start(s);
- }
- } else {
- if (s->state != STATE_INACTIVE) {
- service_stop(s);
+
+ if (s->stop_timeout != 0) {
+ if (s->state == STATE_INACTIVE || s->state == STATE_ERROR) {
+ s->stop_timeout = 0;
+ } else if (time(NULL) - s->stop_timeout >= SV_STOP_TIMEOUT) {
+ printf(":: service '%s' doesn't terminate, killing...\n", s->name);
+ service_kill(s, SIGKILL);
+ s->stop_timeout = 0;
}
+ continue;
+ }
+
+ if (s->state == STATE_INACTIVE && service_need_restart(s)) {
+ service_start(s);
}
}
}
static void control_sockets(void) {
service_t* s;
- char cmd, chr;
- bool read_signo = false;
+ char cmd;
for (int i = 0; i < services_size; i++) {
s = &services[i];
- while (read(s->control, &chr, 1) == 1) {
- printf("handling '%c' from %s\n", chr, s->name);
- if (read_signo) {
- service_handle_command(s, cmd, chr);
- read_signo = false;
- } else if (chr == X_XUP || chr == X_XDOWN) {
- cmd = chr;
- read_signo = true;
- } else {
- service_handle_command(s, cmd, 0);
+ while (read(s->control, &cmd, 1) == 1) {
+ printf("handling '%c' from %s\n", cmd, s->name);
+ service_handle_command(s, cmd);
+ }
+ }
+}
+
+void stop_dummies(void) {
+ bool cont;
+ for (int i = 0; i < services_size; i++) {
+ if (services[i].state != STATE_ACTIVE_DUMMY || services[i].restart == S_RESTART)
+ continue;
+
+ cont = false;
+ for (int i = 0; i < depends_size; i++) {
+ if (depends[i][0] != &services[i])
+ continue;
+
+ if (depends[i][1]->state != STATE_INACTIVE || depends[i][1]->state != STATE_ERROR) {
+ cont = true;
}
}
+ if (!cont) {
+ service_stop(&services[i]);
+ }
}
}
-int service_supervise(const char* service_dir_, const char* runlevel_) {
+int service_supervise(const char* service_dir_, const char* service, bool once) {
struct sigaction sigact = { 0 };
service_t* s;
- time_t start;
- int running;
+
+ daemon_running = true;
sigact.sa_handler = signal_child;
sigaction(SIGCHLD, &sigact, NULL);
sigact.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigact, NULL);
- strncpy(runlevel, runlevel_, SV_NAME_MAX);
service_dir_path = service_dir_;
if ((service_dir = open(service_dir_, O_DIRECTORY)) == -1) {
print_error("error: cannot open directory %s: %s\n", service_dir_);
@@ -108,47 +123,52 @@ int service_supervise(const char* service_dir_, const char* runlevel_) {
null_fd = 1;
}
- printf(":: starting services on '%s'\n", runlevel);
+ printf(":: starting services\n");
- // accept connections and handle requests
- while (daemon_running) {
- service_refresh_directory();
- control_sockets();
- update_services();
- sleep(SV_CHECK_INTERVAL);
- }
+ service_refresh_directory();
+ if ((s = service_get(service)) == NULL) {
+ fprintf(stderr, "error: cannot start '%s': not found\n", service);
+ goto cleanup;
+ }
- printf(":: terminating\n");
+ s->restart = once ? S_ONCE : S_RESTART;
+ service_start(s);
- for (int i = 0; i < services_size; i++) {
- s = &services[i];
- service_stop(s);
- }
- running = 0;
- start = time(NULL);
+ bool cont;
+ // accept connections and handle requests
do {
- sleep(1); // sleep for one second
- // running = 0;
- for (int i = 0; i < services_size; i++) {
- if (services[i].state != STATE_INACTIVE)
- running++;
+ if (!daemon_running) {
+ for (int i = 0; i < services_size; i++) {
+ s = &services[i];
+ service_stop(s);
+ }
}
- printf(":: %d running...\r", running);
- } while (running > 0 && (time(NULL) - start) < SV_STOP_TIMEOUT);
- printf("\n");
+ service_refresh_directory();
+ stop_dummies();
+ control_sockets();
+ update_services();
+
+ sleep(SV_CHECK_INTERVAL);
- for (int i = 0; i < services_size; i++) {
- if (services[i].pid) {
- printf(":: killing %s\n", services[i].name);
- service_kill(&services[i], SIGKILL);
+ cont = false;
+ for (int i = 0; i < services_size; i++) {
+ if (services[i].state != STATE_INACTIVE && services[i].state != STATE_ERROR)
+ cont = true;
}
- }
+ } while (cont);
+
+ printf(":: terminating\n");
printf(":: all services stopped\n");
+cleanup:
+
+ close(service_dir);
+ close(null_fd);
+
signal(SIGPIPE, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
return 0;