commit 49daf6b018e90ebe7af2636ad10ea9431ad68d71
parent a93c555dca8d5012b2cc7d48fa92cdb35d48bb81
Author: Friedel Schön <[email protected]>
Date: Wed, 17 May 2023 01:22:49 +0200
some refactoring + adding runit-support
Diffstat:
27 files changed, 448 insertions(+), 137 deletions(-)
diff --git a/Makefile b/Makefile
@@ -9,7 +9,7 @@ ROFF_DIR := man
# Compiler Options
CC ?= clang
-CFLAGS += -g -std=gnu99 -Wpedantic
+CFLAGS += -g -std=gnu99 -Wpedantic -Wunused-result -Wno-gnu-zero-variadic-macro-arguments
LDFLAGS += -fPIE
# Executable-specific flags
diff --git a/docs/internal/runit-control.txt b/docs/internal/runit-control.txt
@@ -0,0 +1,14 @@
+d = down
+u = up
+x = exit
+t = sig term
+k = sig kill
+p = sig pause
+c = sig cont
+o = once
+a = sig alarm
+h = sig hup
+i = sig int
+q = sig quit
+1 = sig usr1
+2 = sig usr2
+\ No newline at end of file
diff --git a/include/config.h b/include/config.h
@@ -70,6 +70,8 @@
# define SV_ACCEPT_INTERVAL 1 // seconds
#endif
+#define SV_CONTROL_SOCKET "/home/friedel/fiss-%s.sock"
+
// control socket (%s is the runlevel)
#ifndef SV_CONTROL_SOCKET
# define SV_CONTROL_SOCKET "/run/fiss/control-%s.socket"
diff --git a/include/config_parser.h b/include/config_parser.h
@@ -5,4 +5,4 @@
void parse_env_file(char** env);
void parse_param_file(service_t* s, char* args[]);
-pid_t parse_pid_file(service_t* s);
-\ No newline at end of file
+pid_t parse_pid_file(service_t* s);
diff --git a/include/message.h b/include/message.h
@@ -18,4 +18,4 @@ typedef enum prog {
} prog_t;
void print_usage_exit(prog_t prog, int status) __attribute__((noreturn));
-void print_version_exit() __attribute__((noreturn));
-\ No newline at end of file
+void print_version_exit(void) __attribute__((noreturn));
diff --git a/include/pattern.h b/include/pattern.h
@@ -1,3 +1,3 @@
#pragma once
-int pattern_test(const char* pattern, const char* match);
-\ No newline at end of file
+int pattern_test(const char* pattern, const char* match);
diff --git a/include/service.h b/include/service.h
@@ -7,8 +7,9 @@
#include <stdint.h>
#include <time.h>
-#define SV_SERIAL_LEN 17
-#define SV_HAS_LOGSERVICE ((void*) 1)
+#define SV_SERIAL_LEN 20
+#define SV_SERIAL_RUNIT_LEN 20
+#define SV_HAS_LOGSERVICE ((void*) 1)
#define EBADCMD 1 // command not found
#define ENOSV 2 // service required
@@ -29,6 +30,23 @@ typedef enum {
S_DISABLE = 'D', // of extra disable
} sv_command_t;
+typedef enum sv_command_runit {
+ R_DOWN = 'd',
+ R_UP = 'u',
+ R_EXIT = 'x',
+ R_TERM = 't',
+ R_KILL = 'k',
+ R_PAUSE = 'p',
+ R_CONT = 'c',
+ R_ONCE = 'o',
+ R_ALARM = 'a',
+ R_HUP = 'h',
+ R_INT = 'i',
+ R_QUIT = 'q',
+ R_USR1 = '1',
+ R_USR2 = '2',
+} sv_command_runit_t;
+
typedef enum service_state {
STATE_INACTIVE,
STATE_SETUP,
@@ -56,14 +74,16 @@ typedef enum service_restart {
} service_restart_t;
typedef struct service {
- int dir; // dirfd
char name[SV_NAME_MAX]; // name of service
+ int dir; // dirfd
service_state_t 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
service_restart_t restart_manual; // should restart on exit
service_restart_t restart_file; // should restart on exit
+ bool restart_final; // combined restart + dependency (only set client-side)
service_exit_t last_exit; // stopped signaled or exited
int return_code; // return code or signal
uint8_t fail_count; // current fail cound
@@ -91,23 +111,29 @@ extern bool daemon_running;
extern bool verbose;
extern dependency_t depends[];
extern int depends_size;
+extern const char* service_dir_path;
-char service_get_command(const char* command);
-int service_command(char command, char extra, const char* service, service_t* response, int response_max);
-int service_handle_command(void* s, sv_command_t command, uint8_t extra, service_t** response);
-int service_pattern(const char* name, service_t** dest, int dest_max);
-int service_refresh();
-int service_supervise(const char* service_dir, const char* runlevel, bool force_socket);
-service_t* service_get(const char* name);
-service_t* service_register(int dir, const char* name, bool is_log_service);
-void service_check_state(service_t* s, bool signaled, int return_code);
-void service_handle_socket(int client);
-void service_load(service_t* s, const uint8_t* buffer); // for fsvc
-void service_send(service_t* s, int signal);
-void service_start(service_t* s, bool* changed);
-void service_stop(service_t* s, bool* changed);
-void service_store(service_t* s, uint8_t* buffer); // for fsvs
-void service_update_dependency(service_t* s);
-bool service_need_restart(service_t* s);
-void service_run(service_t* s);
-\ No newline at end of file
+char service_get_command(const char* command);
+int service_command(char command, char extra, const char* service, service_t* response, int response_max);
+int service_handle_command(void* s, sv_command_t command, uint8_t extra, service_t** response);
+int service_pattern(const char* name, service_t** dest, int dest_max);
+int service_refresh(void);
+int service_supervise(const char* service_dir, const char* runlevel, bool force_socket);
+service_t* service_get(const char* name);
+service_t* service_register(int dir, const char* name, bool is_log_service);
+void service_check_state(service_t* s, bool signaled, int return_code);
+void service_handle_socket(int client);
+void service_load(service_t* s, const uint8_t* buffer); // for fsvc
+void service_send(service_t* s, int signal);
+void service_start(service_t* s, bool* changed);
+void service_stop(service_t* s, bool* changed);
+void service_store(service_t* s, uint8_t* buffer); // for fsvs
+void service_store_runit(service_t* s, uint8_t* buffer);
+const char* service_store_human(service_t* s);
+void service_update_dependency(service_t* s);
+bool service_need_restart(service_t* s);
+void service_run(service_t* s);
+void service_init_status(service_t* s);
+void service_update_status(service_t* s);
+void service_handle_command_runit(service_t* s, sv_command_runit_t command);
diff --git a/include/signame.h b/include/signame.h
@@ -1,3 +1,3 @@
#pragma once
-int signame(char const* signame);
-\ No newline at end of file
+int signame(char const* signame);
diff --git a/include/user_group.h b/include/user_group.h
@@ -3,4 +3,4 @@
#include <unistd.h>
-int parse_ugid(char* str, uid_t* uid, gid_t* gids);
-\ No newline at end of file
+int parse_ugid(char* str, uid_t* uid, gid_t* gids);
diff --git a/include/wtmp.h b/include/wtmp.h
@@ -8,4 +8,4 @@
# define OUR_UTMP "/run/utmp"
#endif
-void write_wtmp(int boot);
-\ No newline at end of file
+void write_wtmp(int boot);
diff --git a/src/command.c b/src/command.c
@@ -32,8 +32,8 @@ int service_command(char command, char extra, const char* service, service_t* re
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), SV_CONTROL_SOCKET, runlevel);
- int ret = connect(sockfd, (struct sockaddr*) &addr, sizeof(addr));
- if (ret == -1) {
+ int ret;
+ if ((ret = connect(sockfd, (struct sockaddr*) &addr, sizeof(addr))) == -1) {
print_error("error: cannot connect to %s: %s\n", addr.sun_path);
exit(EXIT_FAILURE);
}
@@ -61,4 +61,4 @@ int service_command(char command, char extra, const char* service, service_t* re
close(sockfd);
return res;
-}
-\ No newline at end of file
+}
diff --git a/src/command_handler.c b/src/command_handler.c
@@ -46,6 +46,10 @@ int service_handle_command(void* argv, sv_command_t command, unsigned char extra
if (!changed)
return 0;
+
+ s->status_change = time(NULL);
+ service_update_status(s);
+
response[0] = s;
return 1;
@@ -69,6 +73,10 @@ int service_handle_command(void* argv, sv_command_t command, unsigned char extra
if (!changed)
return 0;
+
+ s->status_change = time(NULL);
+ service_update_status(s);
+
response[0] = s;
return 1;
@@ -83,24 +91,32 @@ int service_handle_command(void* argv, sv_command_t command, unsigned char extra
case S_PAUSE:
if (s == NULL)
return -ENOSV;
+
if (s->state == STATE_INACTIVE || s->paused)
return 0;
s->paused = true;
service_send(s, SIGSTOP);
+ s->status_change = time(NULL);
+ service_update_status(s);
+
response[0] = s;
return 1;
case S_RESUME:
if (s == NULL)
return -ENOSV;
+
if (s->state == STATE_INACTIVE || !s->paused)
return 0;
s->paused = false;
service_send(s, SIGCONT);
+ s->status_change = time(NULL);
+ service_update_status(s);
+
response[0] = s;
return 1;
@@ -113,6 +129,9 @@ int service_handle_command(void* argv, sv_command_t command, unsigned char extra
s->state = STATE_INACTIVE;
+ s->status_change = time(NULL);
+ service_update_status(s);
+
response[0] = s;
return 1;
@@ -139,12 +158,11 @@ int service_handle_command(void* argv, sv_command_t command, unsigned char extra
if (argv == NULL)
return -ENOSV;
-
strcpy(path_buffer, extra == 1 ? "once-" : "up-");
strcat(path_buffer, runlevel);
if (command == S_ENABLE) {
- if ((fd = openat(s->dir, path_buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)
+ if ((fd = openat(s->dir, path_buffer, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1)
return 0;
close(fd);
} else {
diff --git a/src/exec/fsvs.c b/src/exec/fsvs.c
@@ -47,7 +47,7 @@ int main(int argc, char** argv) {
fprintf(stderr, "error: invalid option -%c\n", optopt);
else
fprintf(stderr, "error: invalid option %s\n", argv[optind - 1]);
- print_usage_exit(PROG_FSVC, 1);
+ print_usage_exit(PROG_FSVS, 1);
}
}
@@ -55,13 +55,13 @@ int main(int argc, char** argv) {
argc -= optind;
if (argc == 0) {
fprintf(stderr, "error: missing <service-dir>\n");
- print_usage_exit(PROG_FSVC, 1);
+ print_usage_exit(PROG_FSVS, 1);
} else if (argc == 1) {
fprintf(stderr, "error: missing <runlevel>\n");
- print_usage_exit(PROG_FSVC, 1);
+ print_usage_exit(PROG_FSVS, 1);
} else if (argc > 2) {
fprintf(stderr, "error: too many arguments\n");
- print_usage_exit(PROG_FSVC, 1);
+ print_usage_exit(PROG_FSVS, 1);
}
signal(SIGINT, signal_interrupt);
diff --git a/src/exec/seedrng.c b/src/exec/seedrng.c
@@ -420,7 +420,7 @@ int main(int argc __attribute__((unused)), char* argv[] __attribute__((unused)))
return 1;
}
- dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
+ dfd = open(SEED_DIR, O_DIRECTORY);
if (dfd < 0 || flock(dfd, LOCK_EX) < 0) {
perror("Unable to lock seed directory");
program_ret = 1;
diff --git a/src/message.c b/src/message.c
@@ -44,7 +44,7 @@ void print_usage_exit(prog_t prog, int status) {
exit(status);
}
-void print_version_exit() {
+void print_version_exit(void) {
printf(FISS_VERSION_STRING);
exit(0);
-}
-\ No newline at end of file
+}
diff --git a/src/pattern.c b/src/pattern.c
@@ -35,4 +35,4 @@ int pattern_test(const char* pattern, const char* match) {
}
return 1;
-}
-\ No newline at end of file
+}
diff --git a/src/register.c b/src/register.c
@@ -17,7 +17,6 @@ service_t* service_register(int dir, const char* name, bool is_log_service) {
if ((s = service_get(name)) == NULL) {
s = &services[services_size++];
s->state = STATE_INACTIVE;
- s->status_change = time(NULL);
s->restart_manual = S_DOWN;
s->restart_file = S_DOWN;
s->last_exit = EXIT_NONE;
@@ -35,6 +34,8 @@ service_t* service_register(int dir, const char* name, bool is_log_service) {
}
strncpy(s->name, name, sizeof(s->name));
+
+ service_init_status(s);
}
struct stat st;
@@ -72,6 +73,7 @@ service_t* service_register(int dir, const char* name, bool is_log_service) {
}
s->status_change = time(NULL);
+ service_update_status(s);
return s;
}
diff --git a/src/restart.c b/src/restart.c
@@ -24,12 +24,14 @@ static void do_finish(service_t* s) {
} else {
s->state = STATE_INACTIVE;
}
+
+ s->status_change = time(NULL);
+ service_update_status(s);
}
void service_check_state(service_t* s, bool signaled, int return_code) {
- s->status_change = time(NULL);
- s->pid = 0;
+ s->pid = 0;
if (s->restart_file == S_ONCE)
s->restart_file = S_DOWN;
if (s->restart_manual == S_ONCE)
@@ -104,4 +106,7 @@ void service_check_state(service_t* s, bool signaled, int return_code) {
case STATE_INACTIVE:
printf("warn: %s died but was set to inactive\n", s->name);
}
+
+ s->status_change = time(NULL);
+ service_update_status(s);
}
diff --git a/src/runit.c b/src/runit.c
@@ -0,0 +1,138 @@
+#include "service.h"
+#include "util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+static int runit_signals[] = {
+ [R_ALARM] = SIGALRM,
+ [R_HUP] = SIGHUP,
+ [R_INT] = SIGINT,
+ [R_QUIT] = SIGQUIT,
+ [R_USR1] = SIGUSR1,
+ [R_USR2] = SIGUSR2,
+};
+
+void service_init_status(service_t* s) {
+ int lockfd;
+ struct stat st;
+
+ if (fstatat(s->dir, "supervise", &st, 0) == -1)
+ mkdirat(s->dir, "supervise", 0744);
+
+ if (fstatat(s->dir, "supervise/ok", &st, 0) == -1)
+ mkfifoat(s->dir, "supervise/ok", 0644);
+
+ if (fstatat(s->dir, "supervise/control", &st, 0) == -1)
+ mkfifoat(s->dir, "supervise/control", 0644);
+
+ if (openat(s->dir, "supervise/ok", O_RDONLY | O_NONBLOCK) == -1) {
+ print_error("cannot open supervise/ok: %s\n");
+ return;
+ }
+
+ if ((s->control = openat(s->dir, "supervise/control", O_RDONLY | O_NONBLOCK)) == -1) {
+ print_error("cannot open supervise/ok: %s\n");
+ return;
+ }
+
+ if ((lockfd = openat(s->dir, "supervise/lock", O_CREAT | O_WRONLY, 0644)) == -1) {
+ print_error("cannot create supervise/lock: %s\n");
+ return;
+ }
+ close(lockfd);
+}
+
+void service_update_status(service_t* s) {
+ int fd;
+ if ((fd = openat(s->dir, "supervise/status", O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
+ print_error("cannot open supervise/status: %s\n");
+ return;
+ }
+
+ uint8_t stat_runit[SV_SERIAL_RUNIT_LEN];
+ service_store_runit(s, stat_runit);
+
+ if (write(fd, stat_runit, sizeof(stat_runit)) == -1) {
+ print_error("cannot write to supervise/status: %s\n");
+ return;
+ }
+
+ close(fd);
+
+ if ((fd = openat(s->dir, "supervise/stat", O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
+ print_error("cannot create supervise/stat: %s\n");
+ return;
+ }
+
+ const char* stat_human = service_store_human(s);
+ if (write(fd, stat_human, strlen(stat_human)) == -1) {
+ print_error("cannot write to supervise/stat: %s\n");
+ return;
+ }
+
+ close(fd);
+
+ if ((fd = openat(s->dir, "supervise/pid", O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
+ print_error("cannot create supervise/stat: %s\n");
+ return;
+ }
+
+ dprintf(fd, "%d", s->pid);
+
+ close(fd);
+}
+
+void service_handle_command_runit(service_t* s, sv_command_runit_t command) {
+ switch (command) {
+ case R_DOWN:
+ case R_TERM:
+ s->restart_manual = S_FORCE_DOWN;
+ service_stop(s, NULL);
+ break;
+ case R_UP:
+ s->restart_manual = S_RESTART;
+ service_start(s, NULL);
+ break;
+ case R_ONCE:
+ s->restart_manual = S_ONCE;
+ service_start(s, NULL);
+ break;
+ case R_KILL:
+ s->restart_manual = S_FORCE_DOWN;
+ service_send(s, SIGKILL);
+ break;
+ case R_PAUSE:
+ if (!s->paused) {
+ s->paused = true;
+ service_send(s, SIGSTOP);
+ }
+ break;
+ case R_CONT:
+ if (s->paused) {
+ s->paused = false;
+ service_send(s, SIGCONT);
+ }
+ break;
+ case R_ALARM:
+ case R_HUP:
+ case R_INT:
+ case R_QUIT:
+ case R_USR1:
+ case R_USR2:
+ service_send(s, runit_signals[command]);
+ break;
+ case R_EXIT:
+ // ignored
+ return;
+ }
+
+ s->status_change = time(NULL);
+ service_update_status(s);
+}
diff --git a/src/serialize.c b/src/serialize.c
@@ -2,72 +2,157 @@
void service_store(service_t* s, uint8_t* buffer) {
- buffer[0] = (s->state);
- buffer[1] = (s->pid >> 0) & 0xff;
- buffer[2] = (s->pid >> 8) & 0xff;
- buffer[3] = (s->pid >> 16) & 0xff;
- buffer[4] = (s->pid >> 24) & 0xff;
- buffer[5] = (s->status_change >> 0) & 0xff;
- buffer[6] = (s->status_change >> 8) & 0xff;
- buffer[7] = (s->status_change >> 16) & 0xff;
- buffer[8] = (s->status_change >> 24) & 0xff;
- buffer[9] = (s->status_change >> 32) & 0xff;
- buffer[10] = (s->status_change >> 40) & 0xff;
- buffer[11] = (s->status_change >> 48) & 0xff;
- buffer[12] = (s->status_change >> 56) & 0xff;
- buffer[13] = (s->fail_count);
- buffer[14] = (s->return_code);
- buffer[15] = (s->restart_file << 0) |
- (s->restart_manual << 2);
- buffer[16] = (s->last_exit << 0) |
- (s->paused << 2) |
- (s->is_log_service << 3) |
- ((s->log_service != NULL) << 4);
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // | DIR | PID | STATUS CHANGE |FC|RC|FLAGS|
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // ST = status
+ // DIR = file descriptor to the running directory
+ // PID = pid of the current instance (dependening on state)
+ // STATUS CHANGE = unix timestamp of last update (why tai tho?)
+ // FC = fail count
+ // RC = last return code (0 if not exitted yet)
- // +--+--+--+--+--+--+--+--+
- // |FS|ZO|LS|AU|PS|DE|SG|RS|
- // +--+--+--+--+--+--+--+--+
- // RS = restart if died
- // SG = is signaled
- // DE = is dead
+ // +--+--+--+--+--+--+--+--*--+--+--+--+--+--+--+--+
+ // | STATE |RSFIL|RSMAN|LSTEX|RD|PS|LS|HL| --- |
+ // +--+--+--+--+--+--+--+--*--+--+--+--+--+--+--+--+
+ // RSFIL = restart file (up-<runlevel>; 0 = down, 2 = once, 3 = restart)
+ // RSMAN = restart manual override (0 = down, 1 = force down, 2 = once, 3 = restart)
+ // RD = absolute restart needed, combining above + dependencies
+ // LSTEX = last exit (0 = never exitted, 1 = normally, 2 = signaled)
// PS = paused
- // AU = autostart
- // LS = has log service
- // ZM = is zombie (cannot die)
- // FS = has finish script
+ // LS = is log service
+ // HS = has log service (in struct is pointer but stored as (void*) 1 or (void*) 0)
- // status file
- // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- // | PID | FIN_PID | STATUS_CHANGE |RC|FC|FL|
- // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- // PID = pid of the service (0 if inactive)
- // FIN_PID = pid of the finish script (0 if inactive)
- // STATUS_CHANGE = unix timestamp of the last time the service changed
- // RC = last return code
- // FC = count of failture in a row
- // FL = flags
+ buffer[0] = (s->dir >> 0) & 0xff;
+ buffer[1] = (s->dir >> 8) & 0xff;
+ buffer[2] = (s->dir >> 16) & 0xff;
+ buffer[3] = (s->dir >> 24) & 0xff;
+ buffer[4] = (s->pid >> 0) & 0xff;
+ buffer[5] = (s->pid >> 8) & 0xff;
+ buffer[6] = (s->pid >> 16) & 0xff;
+ buffer[7] = (s->pid >> 24) & 0xff;
+ buffer[8] = (s->status_change >> 0) & 0xff;
+ buffer[9] = (s->status_change >> 8) & 0xff;
+ buffer[10] = (s->status_change >> 16) & 0xff;
+ buffer[11] = (s->status_change >> 24) & 0xff;
+ buffer[12] = (s->status_change >> 32) & 0xff;
+ buffer[13] = (s->status_change >> 40) & 0xff;
+ buffer[14] = (s->status_change >> 48) & 0xff;
+ buffer[15] = (s->status_change >> 56) & 0xff;
+ buffer[16] = (s->fail_count);
+ buffer[17] = (s->return_code);
+ buffer[18] = (s->state << 0) |
+ (s->restart_file << 4) |
+ (s->restart_manual << 6);
+ buffer[19] = (s->last_exit << 0) |
+ (service_need_restart(s) << 2) |
+ (s->paused << 3) |
+ (s->is_log_service << 4) |
+ ((s->log_service != NULL) << 5);
+}
+
+const char* service_store_human(service_t* s) {
+ switch (s->state) {
+ case STATE_SETUP:
+ return "setup";
+ case STATE_STARTING:
+ return "starting";
+ case STATE_ACTIVE_DUMMY:
+ case STATE_ACTIVE_FOREGROUND:
+ case STATE_ACTIVE_BACKGROUND:
+ case STATE_ACTIVE_PID:
+ return "run";
+ case STATE_STOPPING:
+ return "stopping";
+ case STATE_FINISHING:
+ return "finishing";
+ case STATE_INACTIVE:
+ return "down";
+ case STATE_DEAD:
+ return "dead";
+ }
+}
+
+void service_store_runit(service_t* s, uint8_t* buffer) {
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // | TAI SECONDS | TAIA NANO | PID |PS|WU|TR|ST|
+ // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ // TAI SECONDS = unix seconds + 4611686018427387914ULL
+ // TAIA NANO = unix nanoseconds (nulled-out as fiss don't store them)
+ // PID = current pid
+ // PS = is paused (int boolean)
+ // WU = wants up ('u' if want up, 'd' if want down)
+ // TR = was terminated (int boolean)
+ // ST = state (0 is down, 1 is running, 2 is finishing)
+
+ uint64_t tai = s->status_change + 4611686018427387914ULL;
+ int runit_state;
+ switch (s->state) {
+ case STATE_INACTIVE:
+ case STATE_DEAD:
+ runit_state = 0;
+ break;
+ case STATE_SETUP:
+ case STATE_STARTING:
+ case STATE_ACTIVE_DUMMY:
+ case STATE_ACTIVE_FOREGROUND:
+ case STATE_ACTIVE_BACKGROUND:
+ case STATE_ACTIVE_PID:
+ runit_state = 1;
+ break;
+ case STATE_FINISHING:
+ case STATE_STOPPING:
+ runit_state = 2;
+ break;
+ }
+
+ buffer[0] = (tai >> 0) & 0xff;
+ buffer[1] = (tai >> 8) & 0xff;
+ buffer[2] = (tai >> 16) & 0xff;
+ buffer[3] = (tai >> 24) & 0xff;
+ buffer[4] = (tai >> 32) & 0xff;
+ buffer[5] = (tai >> 40) & 0xff;
+ buffer[6] = (tai >> 48) & 0xff;
+ buffer[7] = (tai >> 56) & 0xff;
+ buffer[8] = 0; // not implemented
+ buffer[9] = 0; // not implemented
+ buffer[10] = 0; // not implemented
+ buffer[11] = 0; // not implemented
+ buffer[12] = (s->pid >> 0) & 0xff;
+ buffer[13] = (s->pid >> 8) & 0xff;
+ buffer[14] = (s->pid >> 16) & 0xff;
+ buffer[15] = (s->pid >> 24) & 0xff;
+ buffer[16] = (s->paused);
+ buffer[17] = service_need_restart(s) ? 'u' : 'd';
+ buffer[18] = 0; // not implemented
+ buffer[19] = runit_state;
}
void service_load(service_t* s, const uint8_t* buffer) {
- s->state = buffer[0];
- s->pid = ((uint32_t) buffer[1] << 0) |
- ((uint32_t) buffer[2] << 8) |
- ((uint32_t) buffer[3] << 16) |
- ((uint32_t) buffer[4] << 24);
- s->status_change = ((uint64_t) buffer[5] << 0) |
- ((uint64_t) buffer[6] << 8) |
- ((uint64_t) buffer[7] << 16) |
- ((uint64_t) buffer[8] << 24) |
- ((uint64_t) buffer[9] << 32) |
- ((uint64_t) buffer[10] << 40) |
- ((uint64_t) buffer[11] << 48) |
- ((uint64_t) buffer[12] << 56);
- s->fail_count = buffer[13];
- s->return_code = buffer[14];
- s->restart_file = (buffer[15] >> 0) & 0x03;
- s->restart_manual = (buffer[15] >> 2) & 0x03;
- s->last_exit = (buffer[16] >> 0) & 0x03;
- s->paused = (buffer[16] >> 2) & 0x01;
- s->is_log_service = (buffer[16] >> 3) & 0x01;
- s->log_service = (buffer[16] >> 4) ? (void*) 1 : NULL;
-}
-\ No newline at end of file
+ s->dir = ((uint32_t) buffer[0] << 0) |
+ ((uint32_t) buffer[1] << 8) |
+ ((uint32_t) buffer[2] << 16) |
+ ((uint32_t) buffer[3] << 24);
+ s->pid = ((uint32_t) buffer[4] << 0) |
+ ((uint32_t) buffer[5] << 8) |
+ ((uint32_t) buffer[6] << 16) |
+ ((uint32_t) buffer[7] << 24);
+ s->status_change = ((uint64_t) buffer[8] << 0) |
+ ((uint64_t) buffer[9] << 8) |
+ ((uint64_t) buffer[10] << 16) |
+ ((uint64_t) buffer[11] << 24) |
+ ((uint64_t) buffer[12] << 32) |
+ ((uint64_t) buffer[13] << 40) |
+ ((uint64_t) buffer[14] << 48) |
+ ((uint64_t) buffer[15] << 56);
+ s->fail_count = buffer[16];
+ s->return_code = buffer[17];
+ s->state = (buffer[18] >> 0) & 0x0F;
+ s->restart_file = (buffer[18] >> 4) & 0x03;
+ s->restart_manual = (buffer[18] >> 6) & 0x03;
+ s->last_exit = (buffer[19] >> 0) & 0x03;
+ s->restart_final = (buffer[19] >> 2) & 0x01;
+ s->paused = (buffer[19] >> 3) & 0x01;
+ s->is_log_service = (buffer[19] >> 4) & 0x01;
+ s->log_service = (buffer[19] >> 5) ? (void*) 1 : (void*) 0;
+}
diff --git a/src/service.c b/src/service.c
@@ -16,6 +16,7 @@ service_t services[SV_SERVICE_MAX];
int services_size = 0;
char runlevel[SV_NAME_MAX];
int service_dir;
+const char* service_dir_path;
int control_socket;
int null_fd;
bool verbose = false;
@@ -40,10 +41,11 @@ int service_pattern(const char* name, service_t** dest, int dest_max) {
return size;
}
-int service_refresh() {
+int service_refresh(void) {
DIR* dp;
struct dirent* ep;
- if ((dp = fdopendir(service_dir)) == NULL) {
+
+ if ((dp = opendir(service_dir_path)) == NULL) {
print_error("error: cannot open service directory: %s\n");
return -1;
}
@@ -55,6 +57,7 @@ int service_refresh() {
if (s->pid)
kill(s->pid, SIGKILL);
close(s->dir);
+ close(s->control);
if (i < services_size - 1) {
memmove(services + i, services + i + 1, services_size - i - 1);
i--;
@@ -101,4 +104,4 @@ bool service_need_restart(service_t* s) {
s->restart_manual == S_ONCE ||
s->restart_manual == S_RESTART ||
is_dependency(s);
-}
-\ No newline at end of file
+}
diff --git a/src/socket_handler.c b/src/socket_handler.c
@@ -64,4 +64,4 @@ cleanup:
write(client, "", 1);
}
close(client);
-}
-\ No newline at end of file
+}
diff --git a/src/stage.c b/src/stage.c
@@ -22,7 +22,7 @@ void sigblock_all(bool unblock) {
sigprocmask(unblock, &ss, NULL);
}
-void handle_stage1() {
+void handle_stage1(void) {
int pid, ttyfd, exitstat;
sigset_t ss;
while ((pid = fork()) == -1) {
@@ -117,7 +117,7 @@ void handle_stage1() {
}
-void handle_stage3() {
+void handle_stage3(void) {
int pid, ttyfd, exitstat;
sigset_t ss;
while ((pid = fork()) == -1) {
diff --git a/src/start.c b/src/start.c
@@ -52,7 +52,7 @@ static void set_pipes(service_t* s) {
}
}
-static void set_user() {
+static void set_user(void) {
char buffer[SV_USER_BUFFER];
int user_file;
if ((user_file = open("user", O_RDONLY)) != -1) {
@@ -129,6 +129,7 @@ void service_run(service_t* s) {
}
}
s->status_change = time(NULL);
+ service_update_status(s);
}
void service_start(service_t* s, bool* changed) {
@@ -156,6 +157,8 @@ void service_start(service_t* s, bool* changed) {
print_error("error: cannot execute ./setup: %s\n");
s->state = STATE_INACTIVE;
}
+ s->status_change = time(NULL);
+ service_update_status(s);
} else {
service_run(s);
}
diff --git a/src/stop.c b/src/stop.c
@@ -15,6 +15,9 @@ void service_stop(service_t* s, bool* changed) {
service_check_state(s, false, 0);
if (changed)
*changed = true;
+
+ s->status_change = time(NULL);
+ service_update_status(s);
break;
case STATE_ACTIVE_FOREGROUND:
case STATE_ACTIVE_PID:
@@ -22,6 +25,9 @@ void service_stop(service_t* s, bool* changed) {
kill(s->pid, SIGTERM);
if (changed)
*changed = true;
+
+ s->status_change = time(NULL);
+ service_update_status(s);
break;
case STATE_ACTIVE_BACKGROUND:
s->state = STATE_STOPPING;
@@ -31,6 +37,9 @@ void service_stop(service_t* s, bool* changed) {
}
if (changed)
*changed = true;
+
+ s->status_change = time(NULL);
+ service_update_status(s);
break;
case STATE_STARTING:
case STATE_STOPPING:
@@ -39,6 +48,9 @@ void service_stop(service_t* s, bool* changed) {
if (changed)
*changed = true;
+ s->status_change = time(NULL);
+ service_update_status(s);
+ break;
case STATE_INACTIVE:
case STATE_DEAD:
break;
diff --git a/src/supervise.c b/src/supervise.c
@@ -87,6 +87,18 @@ static void accept_socket(void) {
}
}
+static void control_sockets(void) {
+ service_t* s;
+ char cmd;
+ for (int i = 0; i < services_size; i++) {
+ s = &services[i];
+
+ while (read(s->control, &cmd, 1) == 1) {
+ service_handle_command_runit(s, cmd);
+ }
+ }
+}
+
int service_supervise(const char* service_dir_, const char* runlevel_, bool force_socket) {
struct sigaction sigact = { 0 };
sigact.sa_handler = signal_child;
@@ -95,12 +107,15 @@ int service_supervise(const char* service_dir_, const char* runlevel_, bool forc
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_);
return 1;
}
- setenv("SERVICE_RUNLEVEL", runlevel, true);
+ // setenv("SERVICE_RUNLEVEL", runlevel, true);
+
+ umask(0002);
char socket_path[PATH_MAX];
snprintf(socket_path, PATH_MAX, SV_CONTROL_SOCKET, runlevel);
@@ -110,13 +125,6 @@ int service_supervise(const char* service_dir_, const char* runlevel_, bool forc
null_fd = 1;
}
- printf(":: starting services on '%s'\n", runlevel);
-
- if (service_refresh() < 0)
- return 1;
-
- printf(":: started services\n");
-
struct stat socket_stat;
if (force_socket) {
if (unlink(socket_path) == -1 && errno != ENOENT) {
@@ -154,11 +162,19 @@ int service_supervise(const char* service_dir_, const char* runlevel_, bool forc
print_error("warn: fcntl-setflags on control-socket failed: %s\n");
}
+ printf(":: starting services on '%s'\n", runlevel);
+
+ if (service_refresh() < 0)
+ return 1;
+
+ printf(":: started services\n");
+
// accept connections and handle requests
while (daemon_running) {
check_deaths();
service_refresh();
check_services();
+ control_sockets();
accept_socket();
}
diff --git a/src/util.c b/src/util.c
@@ -74,4 +74,4 @@ int fork_dup_cd_exec(int dir, const char* path, int fd0, int fd1, int fd2) {
_exit(1);
}
return pid;
-}
-\ No newline at end of file
+}