fiss

Friedel's Initialization and Service Supervision
Log | Files | Refs | LICENSE

commit 4af02dd197f3f87625cb241c559c573104dac17b
parent d03e64c11adc6200806f7de3457194c4f05cd3aa
Author: Friedel Schon <[email protected]>
Date:   Tue, 11 Apr 2023 13:30:39 +0200

beautifying fsvc

Diffstat:
Minclude/service.h | 14++++++++------
Ainclude/signame.h | 4++++
Msrc/command.c | 29-----------------------------
Msrc/command_handler.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/exec/finit.c | 1+
Msrc/exec/fsvc.c | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/register.c | 8++++----
Msrc/restart.c | 4++--
Msrc/service.c | 4+++-
Asrc/signame.c | 186+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/socket_handler.c | 27++++++++++++++++-----------
Msrc/supervise.c | 2+-
Msrc/util.c | 2++
13 files changed, 431 insertions(+), 90 deletions(-)

diff --git a/include/service.h b/include/service.h @@ -23,10 +23,11 @@ typedef enum { S_PAUSE = 'p', // pause service (send SIGSTOP) S_RESUME = 'c', // unpause service (send SIGCONT) S_REVIVE = 'v', // revive died service - S_UPDATE = 'g', // force update info // todo S_EXIT = 'x', // kill the fsvs instance S_STATUS = 'a', // get status of all services - S_CHLEVEL = 'l', // change runlevel + S_SWITCH = 'l', // change runlevel + S_ENABLE = 'E', // of extra disable + S_DISABLE = 'D', // of extra disable } sv_command_t; typedef enum service_state { @@ -48,9 +49,10 @@ typedef enum service_exit { } service_exit_t; typedef enum service_restart { - S_NONE, - S_RESTART, + S_DOWN, + S_FORCE_DOWN, // force down (manual) S_ONCE, + S_RESTART, } service_restart_t; typedef struct service { @@ -80,7 +82,7 @@ extern string command_string[]; extern service_t services[]; extern int services_size; -extern string runlevel; +extern char runlevel[]; extern int null_fd; extern int control_socket; extern bool daemon_running; @@ -92,7 +94,7 @@ extern int depends_size; char service_get_command(string command); int service_command(char command, char extra, string service, service_t* response, int response_max); -int service_handle_command(service_t* s, sv_command_t command, uint8_t extra, service_t** response); +int service_handle_command(void* s, sv_command_t command, uint8_t extra, service_t** response); int service_pattern(string name, service_t** dest, int dest_max); int service_refresh(); int service_supervise(string service_dir, string runlevel, bool force_socket); diff --git a/include/signame.h b/include/signame.h @@ -0,0 +1,3 @@ +#pragma once + +int signame(char const* signame); +\ No newline at end of file diff --git a/src/command.c b/src/command.c @@ -16,24 +16,6 @@ string command_error[] = { [EBEXT] = "invalid extra" }; -string command_string[] = { - (void*) S_START, "start", // start if not running and restart if failed - (void*) S_START, "up", // start if not running and restart if failed - (void*) S_STOP, "stop", // stop if running and not restart if failed - (void*) S_STOP, "down", // stop if running and not restart if failed - (void*) S_SEND, "send", // + signal | send signal to service - (void*) S_SEND, "kill", // + signal | send signal to service - (void*) S_PAUSE, "pause", // pause service (send SIGSTOP) - (void*) S_RESUME, "resume", // unpause service (send SIGCONT) - (void*) S_REVIVE, "revive", // revive died service - (void*) S_UPDATE, "update", // force update info // todo - (void*) S_STATUS, "status", // get status of all services - (void*) S_EXIT, "exit", // exit - (void*) S_CHLEVEL, "chlevel", - 0, 0 -}; - - int service_command(char command, char extra, string service, service_t* response, int response_max) { char request[2] = { command, extra }; @@ -77,15 +59,4 @@ int service_command(char command, char extra, string service, service_t* respons close(sockfd); return res; -} - -char service_get_command(string command) { - char cmd_abbr = '\0'; - for (string* cn = command_string; cn[1] != NULL; cn += 2) { - if (streq(command, cn[1])) { - cmd_abbr = (size_t) cn[0]; - break; - } - } - return cmd_abbr; } \ No newline at end of file diff --git a/src/command_handler.c b/src/command_handler.c @@ -1,11 +1,18 @@ #include "service.h" +#include <fcntl.h> +#include <linux/limits.h> #include <signal.h> #include <stdio.h> +#include <string.h> -int service_handle_command(service_t* s, sv_command_t command, unsigned char extra, service_t** response) { - bool changed = false; +int service_handle_command(void* argv, sv_command_t command, unsigned char extra, service_t** response) { + service_t* s = argv; + bool changed = false; + char path_buffer[PATH_MAX]; + int fd; + switch (command) { case S_STATUS: if (s != NULL) { @@ -49,13 +56,12 @@ int service_handle_command(service_t* s, sv_command_t command, unsigned char ext } if (s->state == STATE_INACTIVE) return 0; - if (extra == 1) { // pin - changed = s->restart_manual != S_NONE; - s->restart_manual = S_NONE; + changed = s->restart_manual != S_FORCE_DOWN; + s->restart_manual = S_FORCE_DOWN; } else { - changed = s->restart_manual != S_ONCE; - s->restart_manual = S_ONCE; + changed = s->restart_manual != S_DOWN; + s->restart_manual = S_DOWN; } if (extra == 0 || extra == 1) service_stop(s, &changed); @@ -113,8 +119,42 @@ int service_handle_command(service_t* s, sv_command_t command, unsigned char ext daemon_running = false; return 0; - default: - fprintf(stderr, "warning: handling command: unknown command 0x%2x%2x\n", command, extra); - return -EBADSV; + case S_SWITCH: + if (argv == NULL) + return -ENOSV; + + strcpy(runlevel, argv); + + if (extra == 1) { + for (int i = 0; i < services_size; i++) { + services[i].restart_manual = S_DOWN; + } + } + + return 0; + + case S_ENABLE: + case S_DISABLE: + if (argv == NULL) + return -ENOSV; + + if (extra == 1) // once + snprintf(path_buffer, PATH_MAX, "%s/%s/once-%s", service_dir, s->name, runlevel); + else + snprintf(path_buffer, PATH_MAX, "%s/%s/up-%s", service_dir, s->name, runlevel); + + if (command == S_ENABLE) { + if ((fd = open(path_buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) + return 0; + close(fd); + } else { + if (remove(path_buffer) == -1) + return 0; + } + + response[0] = s; + return 1; } + fprintf(stderr, "warning: handling command: unknown command 0x%2x%2x\n", command, extra); + return -EBADSV; } diff --git a/src/exec/finit.c b/src/exec/finit.c @@ -85,6 +85,7 @@ int main(int argc, string* argv) { service_supervise(SV_SERVICE_DIR, SV_RUNLEVEL, true); sigblock_all(false); } + handle_stage3(); /* reget stderr */ diff --git a/src/exec/fsvc.c b/src/exec/fsvc.c @@ -1,5 +1,6 @@ #include "config.h" #include "service.h" +#include "signame.h" #include <errno.h> #include <fcntl.h> @@ -97,20 +98,26 @@ static const struct option long_options[] = { { "version", no_argument, 0, 'V' }, { "runlevel", no_argument, 0, 'r' }, { "service-dir", no_argument, 0, 's' }, - { "pin", no_argument, 0, 'n' }, - { "now", no_argument, 0, 'p' }, + { "pin", no_argument, 0, 'p' }, + { "once", no_argument, 0, 'o' }, + { "check", no_argument, 0, 'c' }, + { "reset", no_argument, 0, 'f' }, { 0 } }; int main(int argc, char** argv) { - runlevel = getenv(SV_RUNLEVEL_ENV) ?: SV_RUNLEVEL; + strcpy(runlevel, getenv(SV_RUNLEVEL_ENV) ?: SV_RUNLEVEL); service_dir = SV_SERVICE_DIR; + bool check = false, + pin = false, + once = false, + reset = false; int c; - while ((c = getopt_long(argc, argv, ":hVvnps:r:", long_options, NULL)) > 0) { + while ((c = getopt_long(argc, argv, ":hVvs:r:pocf", long_options, NULL)) > 0) { switch (c) { case 'r': - runlevel = optarg; + strcpy(runlevel, optarg); break; case 's': service_dir = optarg; @@ -124,6 +131,18 @@ int main(int argc, char** argv) { case 'h': printf(HELP_MESSAGE, argv[0]); return 0; + case 'p': + pin = true; + break; + case 'o': + once = true; + break; + case 'c': + check = true; + break; + case 'f': + reset = true; + break; case '?': if (optopt) fprintf(stderr, "error: invalid option -%c\n", optopt); @@ -136,42 +155,150 @@ int main(int argc, char** argv) { argv += optind; if (argc < 1) { - printf("%s [options] <command> [service]\n", argv[0]); + printf("fsvc [options] <command> [service]\n"); return 1; } - char* command = argv[0]; - string service = argc >= 2 ? argv[1] : ""; - int extra = argc >= 3 ? atoi(argv[2]) : 0; + string command_str = argv[0]; + argv++; + argc--; + + const char* service = NULL; + char command, extra = 0; + + if (streq(command_str, "up") || streq(command_str, "start") || streq(command_str, "down") || streq(command_str, "stop")) { + for (int i = 0; i < argc; i++) { + if (!service) { + service = argv[i]; + } else { + printf("redundant argument '%s'\n", argv[i]); + return 1; + } + } + if (service == NULL) { + printf("service omitted\n"); + return 1; + } + + command = streq(command_str, "down") || streq(command_str, "stop") ? S_STOP : S_START; + extra = pin; + pin = false; + } else if (streq(command_str, "send") || streq(command_str, "kill")) { + for (int i = 0; i < argc; i++) { + if (!service) { + service = argv[i]; + } else if (!extra) { + char* endptr; + extra = strtol(argv[1], &endptr, 10); + if (endptr == argv[1]) { + if ((extra = signame(argv[1])) == 0) { + printf("unknown signalname\n"); + return 1; + } + } else if (endptr != strchr(argv[1], '\0')) { + printf("malformatted signal\n"); + return 1; + } + } else { + printf("redundant argument '%s'\n", argv[i]); + return 1; + } + } + if (service == NULL) { + printf("service omitted\n"); + return 1; + } + + command = S_SEND; + } else if (streq(command_str, "enable") || streq(command_str, "disable")) { + for (int i = 0; i < argc; i++) { + if (!service) { + service = argv[i]; + } else { + printf("redundant argument '%s'", argv[i]); + return 1; + } + } + if (service == NULL) { + printf("service omitted\n"); + return 1; + } + + command = streq(command_str, "enable") ? S_ENABLE : S_DISABLE; + extra = once; + once = false; + } else if (streq(command_str, "status")) { + for (int i = 0; i < argc; i++) { + if (!service) { + service = argv[i]; + } else { + printf("redundant argument '%s'\n", argv[i]); + return 1; + } + } + + command = S_STATUS; + extra = check; + check = false; + } else if (streq(command_str, "pause") || streq(command_str, "resume")) { + for (int i = 0; i < argc; i++) { + if (!service) { + service = argv[i]; + } else { + printf("redundant argument '%s'", argv[i]); + return 1; + } + } + if (service == NULL) { + printf("service omitted\n"); + return 1; + } + + command = streq(command_str, "pause") ? S_PAUSE : S_RESUME; + } else if (streq(command_str, "switch")) { + for (int i = 0; i < argc; i++) { + if (!service) { + service = argv[i]; + } else { + printf("redundant argument '%s'", argv[i]); + return 1; + } + } + if (service == NULL) { + printf("runlevel omitted\n"); + return 1; + } + + command = S_SWITCH; + extra = reset; + reset = false; + } else { + printf("unknown command '%s'\n", command_str); + return 1; + } service_t response[50]; - int res = 0; + int rc; - if (streq(command, "check")) { + if (check) { service_t s; - int rc; - if ((rc = service_command(S_STATUS, 0, service, &s, 1)) != 1) { - printf("error: %s (errno: %d)\n", command_error[-res], -res); + if ((rc = service_command(command, extra, service, &s, 1)) != 1) { + printf("error: %s (errno: %d)\n", command_error[-rc], -rc); return 1; } return s.state == STATE_ACTIVE_PID || s.state == STATE_ACTIVE_DUMMY || s.state == STATE_ACTIVE_FOREGROUND || s.state == STATE_ACTIVE_BACKGROUND; - } else { - char cmd_abbr; - if ((cmd_abbr = service_get_command(command)) == 0) - res = -EBADCMD; - else - res = service_command(cmd_abbr, extra, service, response, 50); - - if (res < 0) { - printf("error: %s (errno: %d)\n", command_error[-res], -res); + rc = service_command(command, extra, service, response, 50); + + if (rc < 0) { + printf("error: %s (errno: %d)\n", command_error[-rc], -rc); return 1; } - for (int i = 0; i < res; i++) { + for (int i = 0; i < rc; i++) { service_t* log = NULL; if (response[i].log_service) { - for (int j = 0; j < res; j++) { + for (int j = 0; j < rc; j++) { if (strncmp(response[i].name, response[j].name, strlen(response[i].name)) == 0) { log = &response[j]; break; @@ -181,4 +308,4 @@ int main(int argc, char** argv) { print_service(&response[i], log); } } -} +} +\ No newline at end of file diff --git a/src/register.c b/src/register.c @@ -16,8 +16,8 @@ service_t* service_register(string name, bool is_log_service) { s = &services[services_size++]; s->state = STATE_INACTIVE; s->status_change = time(NULL); - s->restart_manual = S_NONE; - s->restart_file = S_NONE; + s->restart_manual = S_DOWN; + s->restart_file = S_DOWN; s->last_exit = EXIT_NONE; s->return_code = 0; s->fail_count = 0; @@ -54,14 +54,14 @@ service_t* service_register(string name, bool is_log_service) { snprintf(path_buffer, PATH_MAX, "%s/%s/once-%s", service_dir, s->name, runlevel); autostart_once = stat(path_buffer, &stat_buf) != -1 && S_ISREG(stat_buf.st_mode); - s->restart_file = S_NONE; + s->restart_file = S_DOWN; if (autostart && autostart_once) { fprintf(stderr, "error: %s is marked for up AND once!\n", s->name); } else if (autostart) { s->restart_file = S_RESTART; } else if (autostart_once) { - if (s->restart_file == S_NONE) + if (s->restart_file == S_DOWN) s->restart_file = S_ONCE; } diff --git a/src/restart.c b/src/restart.c @@ -43,9 +43,9 @@ void service_check_state(service_t* s, bool signaled, int return_code) { s->status_change = time(NULL); s->pid = 0; if (s->restart_file == S_ONCE) - s->restart_file = S_NONE; + s->restart_file = S_DOWN; if (s->restart_manual == S_ONCE) - s->restart_manual = S_NONE; + s->restart_manual = S_DOWN; char path_buffer[PATH_MAX]; struct stat stat_buffer; diff --git a/src/service.c b/src/service.c @@ -21,7 +21,7 @@ service_t services[SV_SERVICE_MAX]; int services_size = 0; -string runlevel; +char runlevel[SV_NAME_MAX]; string service_dir; int control_socket; int null_fd; @@ -104,5 +104,7 @@ static bool is_dependency(service_t* d) { } bool service_need_restart(service_t* s) { + if (s->restart_manual == S_FORCE_DOWN) + return is_dependency(s); return s->restart_file || s->restart_manual || is_dependency(s); } \ No newline at end of file diff --git a/src/signame.c b/src/signame.c @@ -0,0 +1,186 @@ +#include "signame.h" + +#include "util.h" + +#include <string.h> + + +#define SIGNUM_NAME(name) \ + { SIG##name, #name } + +typedef struct signal_name { + int num; + const char* name; +} signal_name_t; + +static signal_name_t SIGNUM_NAME_table[] = { +/* Signals required by POSIX 1003.1-2001 base, listed in + traditional numeric order where possible. */ +#ifdef SIGHUP + SIGNUM_NAME(HUP), +#endif +#ifdef SIGINT + SIGNUM_NAME(INT), +#endif +#ifdef SIGQUIT + SIGNUM_NAME(QUIT), +#endif +#ifdef SIGILL + SIGNUM_NAME(ILL), +#endif +#ifdef SIGTRAP + SIGNUM_NAME(TRAP), +#endif +#ifdef SIGABRT + SIGNUM_NAME(ABRT), +#endif +#ifdef SIGFPE + SIGNUM_NAME(FPE), +#endif +#ifdef SIGKILL + SIGNUM_NAME(KILL), +#endif +#ifdef SIGSEGV + SIGNUM_NAME(SEGV), +#endif +/* On Haiku, SIGSEGV == SIGBUS, but we prefer SIGSEGV to match + strsignal.c output, so SIGBUS must be listed second. */ +#ifdef SIGBUS + SIGNUM_NAME(BUS), +#endif +#ifdef SIGPIPE + SIGNUM_NAME(PIPE), +#endif +#ifdef SIGALRM + SIGNUM_NAME(ALRM), +#endif +#ifdef SIGTERM + SIGNUM_NAME(TERM), +#endif +#ifdef SIGUSR1 + SIGNUM_NAME(USR1), +#endif +#ifdef SIGUSR2 + SIGNUM_NAME(USR2), +#endif +#ifdef SIGCHLD + SIGNUM_NAME(CHLD), +#endif +#ifdef SIGURG + SIGNUM_NAME(URG), +#endif +#ifdef SIGSTOP + SIGNUM_NAME(STOP), +#endif +#ifdef SIGTSTP + SIGNUM_NAME(TSTP), +#endif +#ifdef SIGCONT + SIGNUM_NAME(CONT), +#endif +#ifdef SIGTTIN + SIGNUM_NAME(TTIN), +#endif +#ifdef SIGTTOU + SIGNUM_NAME(TTOU), +#endif + +/* Signals required by POSIX 1003.1-2001 with the XSI extension. */ +#ifdef SIGSYS + SIGNUM_NAME(SYS), +#endif +#ifdef SIGPOLL + SIGNUM_NAME(POLL), +#endif +#ifdef SIGVTALRM + SIGNUM_NAME(VTALRM), +#endif +#ifdef SIGPROF + SIGNUM_NAME(PROF), +#endif +#ifdef SIGXCPU + SIGNUM_NAME(XCPU), +#endif +#ifdef SIGXFSZ + SIGNUM_NAME(XFSZ), +#endif + +/* Unix Version 7. */ +#ifdef SIGIOT + SIGNUM_NAME(IOT), /* Older name for ABRT. */ +#endif +#ifdef SIGEMT + SIGNUM_NAME(EMT), +#endif + +/* USG Unix. */ +#ifdef SIGPHONE + SIGNUM_NAME(PHONE), +#endif +#ifdef SIGWIND + SIGNUM_NAME(WIND), +#endif + +/* Unix System V. */ +#ifdef SIGCLD + SIGNUM_NAME(CLD), +#endif +#ifdef SIGPWR + SIGNUM_NAME(PWR), +#endif + +/* GNU/Linux 2.2 and Solaris 8. */ +#ifdef SIGCANCEL + SIGNUM_NAME(CANCEL), +#endif +#ifdef SIGLWP + SIGNUM_NAME(LWP), +#endif +#ifdef SIGWAITING + SIGNUM_NAME(WAITING), +#endif +#ifdef SIGFREEZE + SIGNUM_NAME(FREEZE), +#endif +#ifdef SIGTHAW + SIGNUM_NAME(THAW), +#endif +#ifdef SIGLOST + SIGNUM_NAME(LOST), +#endif +#ifdef SIGWINCH + SIGNUM_NAME(WINCH), +#endif + +/* GNU/Linux 2.2. */ +#ifdef SIGINFO + SIGNUM_NAME(INFO), +#endif +#ifdef SIGIO + SIGNUM_NAME(IO), +#endif +#ifdef SIGSTKFLT + SIGNUM_NAME(STKFLT), +#endif + +/* OpenBSD. */ +#ifdef SIGTHR + SIGNUM_NAME(THR), +#endif + + { 0, NULL }, +}; + +int signame(char const* signame) { + // startswith SIG, remove that so -SIGKILL == -KILL + if (strncmp(signame, "SIG", 3) == 0) { + signame += 3; + } + + // search for name + for (signal_name_t* sigpair = SIGNUM_NAME_table; sigpair->num > 0; sigpair++) + if (streq(sigpair->name, signame)) + return sigpair->num; + + return 0; +} diff --git a/src/socket_handler.c b/src/socket_handler.c @@ -7,6 +7,7 @@ void service_handle_socket(int client) { char command[2] = { 0, 0 }; char service_name[SV_NAME_MAX]; + read(client, command, sizeof(command)); printf("command: %c\n", command[0]); @@ -15,22 +16,26 @@ void service_handle_socket(int client) { printf("command: %c-%02x with service '%s'\n", command[0], command[1], service_name); - int res = 0; - int res_off = 0; + int res = 0; + int res_off = 0; service_t* response[128]; service_t* request[128]; if (service_len > 0) { - int req_size = service_pattern(service_name, request, 128); - if (req_size == 0) { - res = -EBADSV; - goto cleanup; - } - for (int i = 0; i < req_size; i++) { - res = service_handle_command(request[i], command[0], command[1], response + res_off); - if (res < 0) + if (command[0] == S_SWITCH) { + res = service_handle_command(service_name, command[0], command[1], response + res_off); + } else { + int req_size = service_pattern(service_name, request, 128); + if (req_size == 0) { + res = -EBADSV; goto cleanup; - res_off += res; + } + for (int i = 0; i < req_size; i++) { + res = service_handle_command(request[i], command[0], command[1], response + res_off); + if (res < 0) + goto cleanup; + res_off += res; + } } } else { res = service_handle_command(NULL, command[0], command[1], response); diff --git a/src/supervise.c b/src/supervise.c @@ -100,7 +100,7 @@ int service_supervise(string service_dir_, string runlevel_, bool force_socket) sigact.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sigact, NULL); - runlevel = runlevel_; + strcpy(runlevel, runlevel_); service_dir = service_dir_; setenv("SERVICE_RUNLEVEL", runlevel, true); diff --git a/src/util.c b/src/util.c @@ -34,5 +34,7 @@ ssize_t readstr(int fd, char* str) { } ssize_t writestr(int fd, string str) { + if (str == NULL) + return write(fd, "", 1); return write(fd, str, strlen(str) + 1); } \ No newline at end of file