unix/fiss

removing socket, full runit compilant (c0010b9fb6af43268e8386a54a63accf8345d12c)
Repositories | LICENSE

commit c0010b9fb6af43268e8386a54a63accf8345d12c
parent e9e41609626596f6135cc00a225a0b32d50e1186
Author: Friedel Schön <[email protected]>
Date:   Thu, 25 May 2023 14:17:11 +0200

removing socket, full runit compilant

Diffstat:
Mdocs/internal/command.txt54++++++++++++++++++++----------------------------------
Mdocs/internal/serialize.txt74+++++++++++++++++++++++++++++++-------------------------------------------
Minclude/config.h4++++
Minclude/service.h150++++++++++++++++++++++++++++++-------------------------------------------------
Minclude/util.h4+++-
Msrc/dependency.c4++--
Msrc/exec/chpst.c12+-----------
Msrc/exec/fsvc.c413+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/exec/fsvs.c11++---------
Asrc/exec/init.lnk2++
Dsrc/handle_client.c67-------------------------------------------------------------------
Msrc/handle_command.c115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/handle_exit.c4----
Msrc/register.c45++++++++++++++++++++++++++++++++++++++++++++-
Msrc/runit.c56++++++++++++--------------------------------------------
Dsrc/send_command.c63---------------------------------------------------------------
Msrc/serialize.c141++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/service.c24++++++++++++------------
Msrc/stage.c3+--
Msrc/start.c14+++-----------
Msrc/stop.c13++-----------
Msrc/supervise.c103+++++++++++++------------------------------------------------------------------
Msrc/util.c28++++++++++++++++++++--------
23 files changed, 556 insertions(+), 848 deletions(-)

diff --git a/docs/internal/command.txt b/docs/internal/command.txt @@ -1,37 +1,23 @@ FISS COMMANDS ============= -start (u) [0] <service>: start if not running -start (u) [1] <service>: start if not running and pin as up -start (u) [2] <service>: only pin as restart -stop (d) [0] <service>: stop if running -stop (d) [1] <service>: stop if running and pin as down -stop (d) [2] <service>: only pin as down -send (k) [s] <service>: send signal $s to service -pause (p) [-] <service>: pause service (send SIGSTOP) -resume (c) [-] <service>: unpause service (send SIGCONT) -revive (v) [-] <service>: revive died service -update (g) [-] <service>: force update info // todo -exit (x) [-]: stop all services and kill the fsvs instance -refresh (y) [-]: refresh the service directory -status (a) [-] <service>: get status of the service -status (a) [-]: get status of all services + + new in fiss, - same as in runit, ! different behaviour - -RUNIT COMMANDS -============== - -down (d): stopts the service and pin as stopped -up (u): starts the services and pin as started -exit (x): does nothing (actually exits the runsv instance) -once (o): starts the service but don't pin as started -term (t): same as down -kill (k): sends kill -pause (p): pauses the service -cont (c): resumes the service -alarm (a): sends alarm -hup (h): sends hup -int (i): sends interrupt -quit (q): sends quit -usr1 (1): sends usr1 -usr2 (2): sends usr2 -\ No newline at end of file +- up (u): starts the services, pin as started +- down (d): stops the service, pin as stopped +- once (o): starts the service, pin as started once ++ xup (U): stops the service, don't pin as stopped ++ xdown (D): stops the service, don't pin as stopped +- 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 (fail-count) ++ send (s): sends custom signal, followed by signal-byte +- alarm (a): sends alarm +- hup (h): sends hup +- int (i): sends interrupt +- quit (q): sends quit +- usr1 (1): sends usr1 +- usr2 (2): sends usr2 +! exit (x): does nothing (actually exits the runsv instance) +\ No newline at end of file diff --git a/docs/internal/serialize.txt b/docs/internal/serialize.txt @@ -1,59 +1,47 @@ FISS CONTROL RESPONSE ===================== -+--+-/ /-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -| NAME0 | PID | STATUS CHANGE |FC|RC|FLAGS| -+--+-/ /-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -NAME0 = name of services | null-terminated -PID = pid of the current instance (dependening on state) | big endian -STATUS CHANGE = unix timestamp of last update (why tai tho?) | big endian -FC = fail count + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +FISS: | STATUS CHANGE |ST|RC|FC|FL| PID |PS|RS|FD|SR| + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +RUNIT: | STATUS CHANGE | TAIA NANO | PID |PS|WU|TR|SR| + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +STATUS CHANGE = unix seconds + 4611686018427387914ULL (tai, lower endian) +ST = state (see below) RC = last return code (0 if not exitted yet) +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) +SR = state runit (0 is down, 1 is running, 2 is finishing, not available in daemontools, unused by fiss) + +TAIA NANO = unix nanoseconds (written, never read) +WU = wants up ('u' if want up, 'd' if want down) +TR = was terminated (int boolean) FLAGS ----- -+--+--+--+--+--+--+--+--*--+--+--+--+--+--+--+--+ -| STATE |RSFIL|RSMAN|LSTEX|RD|PS|LS|HL| --- | -+--+--+--+--+--+--+--+--*--+--+--+--+--+--+--+--+ -STATE = state of the service -RSFIL = restart file (up-<runlevel>; 0 = down, 2 = once, 3 = restart) ++--+--+--+--+--+--+--+--+ +| --- |RSFIL|RSMAN|LSTEX| ++--+--+--+--+--+--+--+--+ +RSFIL = restart file (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 -LS = is log service -HS = has log service (in struct is pointer but stored as (void*) 1 or (void*) 0) -- = nothing yet STATE ----- 0 = inactive - is not enabled and does nothing -1 = setup - ./setup is running atm -2 = starting - ./start is running atm -3 = active_dummy - service is considered as started but does nothing -4 = active_foreground - ./run is running -5 = active_background - ./start is done and ./stop is not called yet -6 = active_pid - ./start is done and ./pid is not killed yet +1 = setup - ./setup is running +2 = starting - ./start is running +3 = active_foreground - ./run is running +4 = active_background - ./start is done and ./stop is not called yet +5 = active_dummy - service is considered as started but does nothing +6 = stopping - ./stop is running 7 = finishing - ./finish is running -8 = stopping - ./stop is running -9 = dead - was enabled but failed too much or another error appeared - - - -RUNIT CONTROL RESPONSE -====================== - -+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -| TAI SECONDS | TAIA NANO | PID |PS|WU|TR|ST| -+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ -TAI SECONDS = unix seconds + 4611686018427387914ULL | lower endian! -TAIA NANO = unix nanoseconds (nulled-out as fiss don't store them) -PID = current pid | big endian -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) - -yuup, there are no flags, boolean-flags still use the whole byte (and WU even uses 'u'/'d') -\ No newline at end of file +8 = dead - was enabled but failed too much or another error +\ No newline at end of file diff --git a/include/config.h b/include/config.h @@ -138,3 +138,7 @@ #ifndef SV_RUNIT_COMPAT # define SV_RUNIT_COMPAT 1 #endif + +#ifndef SV_STATUS_WAIT +# define SV_STATUS_WAIT 5 +#endif diff --git a/include/service.h b/include/service.h @@ -7,86 +7,64 @@ #include <stdint.h> #include <time.h> -#define EBADCMD 1 // command not found -#define ENOSV 2 // service required -#define EBADSV 3 // no matching services -#define EBEXT 4 // invalid extra - -typedef enum { - S_START = 'u', // start if not running and restart if failed - S_STOP = 'd', // stop if running and not restart if failed - S_SEND = 'k', // + signal | send signal to service - S_PAUSE = 'p', // pause service (send SIGSTOP) - S_RESUME = 'c', // unpause service (send SIGCONT) - S_RESET = 'v', // revive died service - S_EXIT = 'x', // kill the fsvs instance - S_STATUS = 'a', // get status of all services - S_SWITCH = 'l', // change runlevel - S_ENABLE = 'E', // of extra disable - 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_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 + X_PAUSE = 'p', // pauses the service + X_CONT = 'c', // resumes the service + X_RESET = 'r', // resets the service + X_ALARM = 'a', // sends alarm + X_HUP = 'h', // sends hup + X_INT = 'i', // sends interrupt + X_QUIT = 'q', // sends quit + X_USR1 = '1', // sends usr1 + X_USR2 = '2', // sends usr2 + X_EXIT = 'x', // does nothing +} service_command_t; typedef enum service_state { - STATE_INACTIVE, - STATE_SETUP, - STATE_STARTING, - STATE_ACTIVE_DUMMY, - STATE_ACTIVE_FOREGROUND, - STATE_ACTIVE_BACKGROUND, - STATE_ACTIVE_PID, - STATE_FINISHING, - STATE_STOPPING, - STATE_DEAD, + STATE_INACTIVE = 0, + STATE_SETUP = 1, + STATE_STARTING = 2, + STATE_ACTIVE_FOREGROUND = 3, + STATE_ACTIVE_BACKGROUND = 4, + STATE_ACTIVE_DUMMY = 5, + STATE_STOPPING = 6, + STATE_FINISHING = 7, + STATE_DEAD = 8, } service_state_t; typedef enum service_exit { - EXIT_NONE, - EXIT_NORMAL, - EXIT_SIGNALED, + EXIT_NONE = 0, + EXIT_NORMAL = 1, + EXIT_SIGNALED = 2, } service_exit_t; typedef enum service_restart { - S_DOWN, - S_FORCE_DOWN, // force down (manual) - S_ONCE, - S_RESTART, + S_DOWN = 0, + S_FORCE_DOWN = 1, // force down (manual) + S_ONCE = 2, + S_RESTART = 3, } service_restart_t; typedef struct service_serial { - uint8_t pid[4]; - uint8_t status_change[8]; - uint8_t failcount[1]; - uint8_t return_code[1]; - uint8_t flags[2]; -} service_serial_t; - -typedef struct service_serial_runit { uint8_t status_change[8]; - uint8_t status_change_nsec[4]; + uint8_t state; + uint8_t return_code; + uint8_t fail_count; + uint8_t flags; uint8_t pid[4]; - uint8_t paused[1]; - uint8_t wants_up[1]; - uint8_t terminated[1]; - uint8_t state[1]; -} service_serial_runit_t; + uint8_t paused; + uint8_t restart; + uint8_t force_down; + uint8_t state_runit; +} service_serial_t; typedef struct service { char name[SV_NAME_MAX]; // name of service @@ -107,38 +85,23 @@ typedef struct service { struct service* log_service; } service_t; -typedef struct dependency { - service_t* service; - service_t* depends; -} dependency_t; - - -extern const char* command_error[]; -extern const char* command_string[]; +extern service_t services[]; +extern int services_size; +extern char runlevel[]; +extern int service_dir; +extern int null_fd; +extern bool daemon_running; +extern service_t* depends[][2]; +extern int depends_size; +extern const char* service_dir_path; -extern service_t services[]; -extern int services_size; -extern char runlevel[]; -extern int service_dir; -extern int null_fd; -extern int control_socket; -extern bool daemon_running; -extern bool verbose; -extern dependency_t depends[]; -extern int depends_size; -extern const char* service_dir_path; - -const char* service_status_name(service_t* s); void service_decode(service_t* s, const service_serial_t* buffer); // for fsvc void service_encode(service_t* s, service_serial_t* buffer); // for fsvs -void service_encode_runit(service_t* s, service_serial_runit_t* buffer); service_t* service_get(const char* name); void service_handle_client(int client); -int service_handle_command(void* argv, sv_command_t command, uint8_t extra, service_t** response); -void service_handle_command_runit(service_t* s, sv_command_runit_t command); +void service_handle_command(service_t* s, service_command_t command, char data); void service_handle_exit(service_t* s, bool signaled, int return_code); -void service_init_runit(service_t* s); void service_kill(service_t* s, int signal); bool service_need_restart(service_t* s); int service_pattern(const char* name, service_t** dest, int dest_max); @@ -147,8 +110,9 @@ service_t* service_register(int dir, const char* name, bool is_log_service); void service_run(service_t* s); int service_send_command(char command, char extra, const char* service, service_t* response, int response_max); void service_stage(int stage); -void service_start(service_t* s, bool* changed); -void service_stop(service_t* s, bool* changed); +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, bool force_socket); void service_update_dependency(service_t* s); void service_update_status(service_t* s); diff --git a/include/util.h b/include/util.h @@ -21,5 +21,7 @@ unsigned int stat_mode(const char* format, ...); int fork_dup_cd_exec(int dir, const char* path, int fd0, int fd1, int fd2); -int reclaim_console(void); +int reclaim_console(void); void sigblock_all(int unblock); + +long parse_long(const char* str, const char* name); diff --git a/src/dependency.c b/src/dependency.c @@ -13,8 +13,8 @@ void service_add_dependency(service_t* s, service_t* d) { if (s == d) return; - depends[depends_size].service = s; - depends[depends_size].depends = d; + depends[depends_size][0] = s; + depends[depends_size][1] = d; depends_size++; } diff --git a/src/exec/chpst.c b/src/exec/chpst.c @@ -9,16 +9,6 @@ #include <sys/file.h> -static long parse_long(const char* str) { - char* end; - long l = strtol(str, &end, 10); - if (*end != '\0') { - fprintf(stderr, "error: invalid limit '%s'\n", optarg); - exit(1); - } - return l; -} - int main(int argc, char** argv) { int opt, lockfd, lockflags, gid_len = 0; char *arg0 = NULL, *root = NULL, *cd = NULL, *lock = NULL, *exec = NULL; @@ -57,7 +47,7 @@ int main(int argc, char** argv) { cd = optarg; break; case 'n': - nicelevel = parse_long(optarg); + nicelevel = parse_long(optarg, "nice-level"); break; case 'l': lock = optarg; diff --git a/src/exec/fsvc.c b/src/exec/fsvc.c @@ -1,314 +1,207 @@ #include "config.h" +#include "message.h" #include "service.h" -#include "signame.h" +#include "util.h" -#include <ctype.h> +#include <fcntl.h> #include <getopt.h> -#include <libgen.h> -#include <stdio.h> -#include <stdlib.h> +#include <stdbool.h> #include <string.h> +#include <sys/stat.h> +#include <unistd.h> +struct ident { + const char* name; + const char* command; +}; -static const char HELP_MESSAGE[] = - "Usage:\n" - " %s <command> [-cfopqvV] [-r ..] [-s ..] [service]\n" - " /etc/init.d/<service> [-cfopqvV] [-r ..] [-s ..] <command>\n" - "\n" - "Check the manual (fsvc 8) for more information.\n"; +static struct ident command_names[] = { + { "up", "u" }, // starts the services, pin as started + { "down", "d" }, // stops the service, pin as stopped + { "xup", "U" }, // stops the service, don't pin as stopped + { "xdown", "D" }, // stops the service, don't 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 + { "exit", "x" }, // does nothing + { "+up", "U0" }, // starts the service, don't modify pin + { "+down", "D0" }, // stops the service, don't modify pin + { "restart", "!D0U0" }, // 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 + { 0, 0 } +}; -static const char VERSION_MESSAGE[] = - "FISS v" SV_VERSION "\n"; +static const struct option long_options[] = { + { "version", no_argument, NULL, 'V' }, + { "wait", no_argument, NULL, 'w' }, + { 0 } +}; +static char* progname(char* path) { + char* match; + for (;;) { + if ((match = strrchr(path, '/')) == NULL) + return path; -void print_status(service_t* s, char* state, size_t size) { - switch (s->state) { - case STATE_SETUP: - strncpy(state, "setup", size); - break; - case STATE_INACTIVE: - strncpy(state, "inactive", size); - break; - case STATE_STARTING: - strncpy(state, "starting", size); - break; - case STATE_ACTIVE_PID: - snprintf(state, size, "active (pid) as %d", s->pid); - break; - case STATE_ACTIVE_BACKGROUND: - strncpy(state, "active (background)", size); - break; - case STATE_ACTIVE_DUMMY: - strncpy(state, "active (dummy)", size); - break; - case STATE_ACTIVE_FOREGROUND: - snprintf(state, size, "active as %d", s->pid); - break; - case STATE_FINISHING: - strncpy(state, "finishing", size); - break; - case STATE_STOPPING: - strncpy(state, "stopping", size); - break; - case STATE_DEAD: - strncpy(state, "dead", size); - break; - } - time_t diff = time(NULL) - s->status_change; - const char* diff_unit = "sec"; - if (diff >= 60) { - diff /= 60; - diff_unit = "min"; - } - if (diff >= 60) { - diff /= 60; - diff_unit = "hours"; - } - if (diff >= 24) { - diff /= 24; - diff_unit = "days"; + if (match[1] != '\0') + return match + 1; + + *match = '\0'; } - int len = strlen(state); - snprintf(state + len, size - len, " since %lu%s", diff, diff_unit); + return path; } -void print_service(service_t* s) { - char state[100]; - print_status(s, state, sizeof(state)); - - printf("- %s (%s)\n", s->name, state); - printf(" [ %c ] restart on exit\n", s->restart_final ? 'x' : ' '); - if (s->return_code > 0) - printf(" [%3d] last return code (%s)\n", s->return_code, s->last_exit == EXIT_SIGNALED ? "signaled" : "exited"); - printf(" [%3d] fail count\n", s->fail_count); - printf(" [ %c ] has log service\n", s->is_log_service ? '-' - : s->log_service ? 'x' - : ' '); - printf(" [ %c ] is paused\n", s->paused ? 'x' : ' '); - printf("\n"); +static int check_service(int dir) { + int fd; + if ((fd = openat(dir, "supervise/ok", O_WRONLY | O_NONBLOCK)) == -1) + return -1; + close(fd); + return 0; } -static char* to_upper(char* str) { - for (char* c = str; *c; c++) { - if (islower(*c)) - *c += 'A' - 'a'; - } - return str; +static time_t get_mtime(int dir) { + struct stat st; + if (fstatat(dir, "supervise/status", &st, 0) == -1) + return -1; + return st.st_mtim.tv_sec; } -void print_service_short(service_t* s) { - bool active = s->state == STATE_ACTIVE_BACKGROUND || - s->state == STATE_ACTIVE_DUMMY || - s->state == STATE_ACTIVE_FOREGROUND || - s->state == STATE_ACTIVE_PID; - - bool wants_other = active != s->restart_final; - - if (s->state == STATE_SETUP || s->state == STATE_STARTING) - printf("[ {+}]"); - else if (s->state == STATE_FINISHING || s->state == STATE_STOPPING) - printf("[{-} ]"); - else if (active) - printf("[ %s + ]", wants_other ? "<-" : " "); - else - printf("[ - %s ]", wants_other ? "->" : " "); - - printf(" %s", s->name); - - if (s->pid) { - if (s->state == STATE_ACTIVE_PID || s->state == STATE_ACTIVE_FOREGROUND) { - printf(" (pid: %d)", s->pid); - } else if (s->last_exit == EXIT_SIGNALED) { - printf(" (last exit: SIG%s)", sigabbr(s->return_code)); - } else if (s->last_exit == EXIT_NORMAL) { - printf(" (last exit: %d)", s->return_code); - } - } - printf("\n"); +static int send_command(int dir, const char* command) { + int fd; + if ((fd = openat(dir, "supervise/control", O_WRONLY | O_NONBLOCK)) == -1) + return -1; + + if (write(fd, command, strlen(command)) == -1) + return -1; + + close(fd); + + return 0; } -static const struct option long_options[] = { - { "verbose", no_argument, 0, 'v' }, - { "version", no_argument, 0, 'V' }, - { "runlevel", no_argument, 0, 'r' }, - { "pin", no_argument, 0, 'p' }, - { "once", no_argument, 0, 'o' }, - { "check", no_argument, 0, 'c' }, - { "reset", no_argument, 0, 'f' }, - { "short", no_argument, 0, 'q' }, - { 0 } -}; +int status(int dir) { + int fd; + if ((fd = openat(dir, "supervise/status", O_RDONLY | O_NONBLOCK)) == -1) + return -1; -int main(int argc, char** argv) { - strncpy(runlevel, getenv(SV_RUNLEVEL_DEFAULT_ENV) ? getenv(SV_RUNLEVEL_DEFAULT_ENV) : SV_RUNLEVEL_DEFAULT, SV_NAME_MAX); + service_serial_t buffer; + service_t s; - char* argexec = argv[0]; + if (read(fd, &buffer, sizeof(buffer)) == -1) { + close(fd); + return -1; + } - bool check = false, - pin = false, - once = false, - reset = false, + close(fd); - short_ = false; + service_decode(&s, &buffer); - int c; - while ((c = getopt_long(argc, argv, ":Vvqr:pocf", long_options, NULL)) > 0) { - switch (c) { - case 'r': - strncpy(runlevel, optarg, SV_NAME_MAX); - break; - case 'q': - short_ = true; - break; - case 'v': - verbose = true; - break; + printf("%d\n", s.pid); + + return 0; +} + +int main(int argc, char** argv) { + int opt; + int timeout = SV_STATUS_WAIT; + + const char* command = NULL; + + while ((opt = getopt_long(argc, argv, ":Vw:", long_options, NULL)) != -1) { + switch (opt) { case 'V': - printf(VERSION_MESSAGE); - return 0; - case 'p': - pin = true; - break; - case 'o': - once = true; + // version break; - case 'c': - check = true; - break; - case 'f': - reset = true; + case 'w': + timeout = parse_long(optarg, "seconds"); break; + default: case '?': if (optopt) fprintf(stderr, "error: invalid option -%c\n", optopt); else fprintf(stderr, "error: invalid option %s\n", argv[optind - 1]); - fprintf(stderr, "%s", HELP_MESSAGE); - return 1; + print_usage_exit(PROG_FSVC, 1); } } - argc -= optind; - argv += optind; - - if (argc < 1) { - printf("fsvc [options] <command> [service]\n"); - return 1; - } - - const char* command_str = argv[0]; - argv++, argc--; - const char* service; - char command, extra = 0; + argc -= optind, argv += optind; - if (streq(service = basename(argexec), "fsvc")) { - if (argc > 0) { - service = argv[0]; - argv++, argc--; - } else { - service = NULL; + if (argc == 0) { + 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; + break; } } + if (command == NULL) { + fprintf(stderr, "error: unknown command '%s'\n", argv[0]); + print_usage_exit(PROG_FSVC, 1); + } - if (streq(command_str, "up") || streq(command_str, "start") || streq(command_str, "down") || streq(command_str, "stop")) { - if (!service) { - printf("service omitted\n"); - return 1; - } + argc--, argv++; - 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")) { - if (!service) { - printf("service omitted\n"); - return 1; - } - if (argc == 1) { - printf("signal omitted\n"); - return 1; - } - if ((extra = signame(argv[1])) == -1) { - printf("unknown signalname\n"); - return 1; - } + if (argc == 0) { + fprintf(stderr, "error: at least one service must be specified\n"); + print_usage_exit(PROG_FSVC, 1); + } - command = S_SEND; - } else if (streq(command_str, "enable") || streq(command_str, "disable")) { - if (!service) { - printf("service omitted\n"); - return 1; - } + chdir(SV_SERVICE_DIR); - command = streq(command_str, "enable") ? S_ENABLE : S_DISABLE; - extra = once; - once = false; - } else if (streq(command_str, "status")) { - command = S_STATUS; - extra = check; - check = false; - } else if (streq(command_str, "pause") || streq(command_str, "resume")) { - if (!service) { - printf("service omitted\n"); - return 1; - } + bool print_status; + if ((print_status = command[0] == '!')) { + command++; + } - command = streq(command_str, "pause") ? S_PAUSE : S_RESUME; - } else if (streq(command_str, "reset")) { - if (!service) { - printf("service omitted\n"); - return 1; + int dir; + time_t mod, start; + for (int i = 0; i < argc; i++) { + if ((dir = open(argv[i], O_DIRECTORY)) == -1) { + fprintf(stderr, "warning: '%s' is not a valid directory\n", argv[0]); + continue; } - command = S_RESET; - extra = 0; - } else if (streq(command_str, "switch")) { - if (!service) { - printf("runlevel omitted\n"); - return 1; + if (check_service(dir) == -1) { + fprintf(stderr, "warning: '%s' is not a valid service\n", argv[0]); + continue; } - command = S_SWITCH; - extra = reset; - reset = false; - } else { - printf("unknown command '%s'\n", command_str); - return 1; - } - - if (check) - printf("warn: --check specified but not used\n"); - if (pin) - printf("warn: --pin specified but not used\n"); - if (once) - printf("warn: --once specified but not used\n"); - if (reset) - printf("warn: --reset specified but not used\n"); - - - service_t response[SV_SOCKET_SERVICE_MAX]; - int rc; - - if (check) { - service_t s; - if ((rc = service_send_command(command, extra, service, &s, 1)) != 1) { - printf("error: %s (errno: %d)\n", command_error[-rc], -rc); - return 1; + if ((mod = get_mtime(dir)) == -1) { + fprintf(stderr, "warning: cannot get modify-time of '%s'\n", argv[0]); + continue; } - return s.state == STATE_ACTIVE_PID || s.state == STATE_ACTIVE_DUMMY || s.state == STATE_ACTIVE_FOREGROUND || s.state == STATE_ACTIVE_BACKGROUND; - } else { - rc = service_send_command(command, extra, service, response, SV_SOCKET_SERVICE_MAX); - if (rc < 0) { - printf("error: %s (errno: %d)\n", command_error[-rc], -rc); - return 1; + if (command[0] != '\0') { + if (send_command(dir, command) == -1) { + fprintf(stderr, "warning: unable to send command to '%s'\n", argv[0]); + continue; + } + } else { + mod++; // avoid modtime timeout } - for (int i = 0; i < rc; i++) { - if (short_) - print_service_short(&response[i]); - else - print_service(&response[i]); + start = time(NULL); + if (print_status) { + while (get_mtime(dir) == mod && time(NULL) - start < timeout) + usleep(500); // sleep half a secound + + printf(get_mtime(dir) == mod ? "timeout: %s: " : "ok: %s: ", progname(argv[i])); + if (status(dir) == -1) + printf("unable to access supervise/status\n"); } } } diff --git a/src/exec/fsvs.c b/src/exec/fsvs.c @@ -10,13 +10,8 @@ #include <sys/wait.h> #include <unistd.h> -#define SV_DEPENDS_MAX_STR static_stringify(SV_DEPENDS_MAX) -#define MAX_SERVICE_STR static_stringify(SV_SERVICE_MAX) -#define SV_STOP_TIMEOUT_STR static_stringify(SV_STOP_TIMEOUT) - static const struct option long_options[] = { - { "verbose", no_argument, 0, 'v' }, { "version", no_argument, 0, 'V' }, { "force", no_argument, 0, 'f' }, { 0 } @@ -32,16 +27,14 @@ int main(int argc, char** argv) { bool force_socket = false; int c; - while ((c = getopt_long(argc, argv, ":vVf", long_options, NULL)) > 0) { + while ((c = getopt_long(argc, argv, ":Vf", long_options, NULL)) > 0) { switch (c) { - case 'v': - verbose = true; - break; case 'V': print_version_exit(); case 'f': force_socket = true; break; + default: case '?': if (optopt) fprintf(stderr, "error: invalid option -%c\n", optopt); diff --git a/src/exec/init.lnk b/src/exec/init.lnk @@ -0,0 +1 @@ +finit +\ No newline at end of file diff --git a/src/handle_client.c b/src/handle_client.c @@ -1,67 +0,0 @@ -#include "service.h" - -#include <stdio.h> -#include <unistd.h> - - -void service_handle_client(int client) { - char command[2] = { 0, 0 }; - char service_name[SV_NAME_MAX]; - - read(client, command, sizeof(command)); - - ssize_t service_len = readstr(client, service_name); - - if (service_len) - printf("command issued by user: %c-%02x with service '%s'\n", command[0], command[1], service_name); - else - printf("command issued by user: %c-%02x without service\n", command[0], command[1]); - - int res = 0; - int res_off = 0; - service_t* response[SV_SOCKET_SERVICE_MAX]; - service_t* request[SV_SOCKET_SERVICE_MAX]; - - if (service_len > 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; - } - 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); - if (res < 0) - goto cleanup; - - res_off = res; - } - - -cleanup: - if (res < 0) { - res *= -1; - write(client, &res, 1); - goto cleanup; - } else { - write(client, "", 1); - service_serial_t service_buffer; - - for (int i = 0; i < res_off; i++) { - service_encode(response[i], &service_buffer); - writestr(client, response[i]->name); - write(client, &service_buffer, sizeof(service_buffer)); - } - write(client, "", 1); - } - close(client); -} diff --git a/src/handle_command.c b/src/handle_command.c @@ -8,11 +8,11 @@ #include <unistd.h> -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; +#if 0 +int service_handle_command(service_t* s, service_command_t command) { + bool changed = false; + char path_buffer[PATH_MAX]; + int fd; switch (command) { case S_STATUS: @@ -180,3 +180,108 @@ int service_handle_command(void* argv, sv_command_t command, unsigned char extra fprintf(stderr, "warning: handling command: unknown command 0x%2x%2x\n", command, extra); return -EBADSV; } +#endif + +static int runit_signals[] = { + [X_ALARM] = SIGALRM, + [X_HUP] = SIGHUP, + [X_INT] = SIGINT, + [X_QUIT] = SIGQUIT, + [X_USR1] = SIGUSR1, + [X_USR2] = SIGUSR2, +}; + +void service_handle_command(service_t* s, service_command_t command, char data) { + switch (command) { + case X_UP: + s->restart_manual = S_RESTART; + service_start(s); + break; + case X_ONCE: + s->restart_manual = 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; + } + service_stop(s); + break; + case X_KILL: + s->restart_manual = S_FORCE_DOWN; + service_kill(s, SIGKILL); + break; + case X_PAUSE: + if (!s->paused) { + s->paused = true; + service_kill(s, SIGSTOP); + } + break; + case X_CONT: + if (s->paused) { + s->paused = false; + service_kill(s, SIGCONT); + } + break; + case X_ALARM: + case X_HUP: + case X_INT: + case X_QUIT: + case X_USR1: + case X_USR2: + service_kill(s, runit_signals[command]); + break; + case X_RESET: + if (s->paused) { + s->paused = false; + service_kill(s, SIGCONT); + } + + s->fail_count = 0; + if (s->state == STATE_DEAD) + s->state = STATE_INACTIVE; + + s->status_change = time(NULL); + service_update_status(s); + break; + case X_EXIT: + // ignored + return; + } + + s->status_change = time(NULL); + service_update_status(s); +} diff --git a/src/handle_exit.c b/src/handle_exit.c @@ -65,7 +65,6 @@ void service_handle_exit(service_t* s, bool signaled, int return_code) { break; case STATE_ACTIVE_DUMMY: - case STATE_ACTIVE_PID: case STATE_ACTIVE_BACKGROUND: case STATE_STOPPING: do_finish(s); @@ -83,9 +82,6 @@ void service_handle_exit(service_t* s, bool signaled, int return_code) { if (!signaled && return_code == 0) { if (fstatat(s->dir, "stop", &st, 0) != -1 && st.st_mode & S_IXUSR) { s->state = STATE_ACTIVE_BACKGROUND; - } else if (fstatat(s->dir, "pid", &st, 0) != -1 && st.st_mode & S_IXUSR) { - s->pid = parse_pid_file(s); - s->state = STATE_ACTIVE_PID; } else { do_finish(s); } diff --git a/src/register.c b/src/register.c @@ -11,6 +11,49 @@ #include <unistd.h> +static int fd_set_flag(int fd, int flags) { + int rc; + if ((rc = fcntl(fd, F_GETFL)) == -1) + return -1; + + if (fcntl(fd, F_SETFL, rc | flags) == -1) + return -1; + + return 0; +} + +static void init_supervise(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; + } + + fd_set_flag(s->control, O_NONBLOCK); + + if ((lockfd = openat(s->dir, "supervise/lock", O_CREAT | O_WRONLY, 0644)) == -1) { + print_error("cannot create supervise/lock: %s\n"); + return; + } + close(lockfd); +} + service_t* service_register(int dir, const char* name, bool is_log_service) { service_t* s; @@ -35,7 +78,7 @@ service_t* service_register(int dir, const char* name, bool is_log_service) { strncpy(s->name, name, sizeof(s->name)); - service_init_runit(s); + init_supervise(s); s->status_change = time(NULL); service_update_status(s); diff --git a/src/runit.c b/src/runit.c @@ -11,56 +11,23 @@ static int runit_signals[] = { - [R_ALARM] = SIGALRM, - [R_HUP] = SIGHUP, - [R_INT] = SIGINT, - [R_QUIT] = SIGQUIT, - [R_USR1] = SIGUSR1, - [R_USR2] = SIGUSR2, + [X_ALARM] = SIGALRM, + [X_HUP] = SIGHUP, + [X_INT] = SIGINT, + [X_QUIT] = SIGQUIT, + [X_USR1] = SIGUSR1, + [X_USR2] = SIGUSR2, }; -void service_init_runit(service_t* s) { -#if SV_RUNIT_COMPAT != 0 - 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); -#endif -} - void service_update_status(service_t* s) { -#if SV_RUNIT_COMPAT != 0 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; } - service_serial_runit_t stat_runit; - service_encode_runit(s, &stat_runit); + service_serial_t stat_runit; + service_encode(s, &stat_runit); if (write(fd, &stat_runit, sizeof(stat_runit)) == -1) { print_error("cannot write to supervise/status: %s\n"); @@ -90,11 +57,11 @@ void service_update_status(service_t* s) { dprintf(fd, "%d", s->pid); close(fd); -#endif } +#if 0 void service_handle_command_runit(service_t* s, sv_command_runit_t command) { -#if SV_RUNIT_COMPAT != 0 +# if SV_RUNIT_COMPAT != 0 switch (command) { case R_DOWN: case R_TERM: @@ -140,5 +107,6 @@ void service_handle_command_runit(service_t* s, sv_command_runit_t command) { s->status_change = time(NULL); service_update_status(s); -#endif +# endif } +#endif diff --git a/src/send_command.c b/src/send_command.c @@ -1,63 +0,0 @@ -#include "service.h" -#include "util.h" - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> - - -const char* command_error[] = { - [0] = "success", - [EBADCMD] = "command not found", - [ENOSV] = "service required", - [EBADSV] = "no matching services", - [EBEXT] = "invalid extra" -}; - -int service_send_command(char command, char extra, const char* service, service_t* response, int response_max) { - char request[2] = { command, extra }; - - int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sockfd == -1) { - print_error("error: cannot connect to socket: %s\n"); - exit(1); - } - - struct sockaddr_un addr; - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), SV_CONTROL_SOCKET, runlevel); - - 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); - } - - write(sockfd, request, sizeof(request)); - writestr(sockfd, service); - - int res; - read(sockfd, &res, 1); - - service_serial_t service_buffer; - - if (res == 0) { - if (response) { - while (res < response_max && readstr(sockfd, response[res].name) > 1) { - read(sockfd, &service_buffer, sizeof(service_buffer)); - service_decode(&response[res], &service_buffer); - res++; - } - } - } else { - res *= -1; - } - - close(sockfd); - return res; -} diff --git a/src/serialize.c b/src/serialize.c @@ -7,15 +7,16 @@ const char* service_status_name(service_t* s) { 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_ACTIVE_BACKGROUND: + return "run (background)"; + case STATE_ACTIVE_DUMMY: + return "run (dummy)"; case STATE_FINISHING: return "finishing"; + case STATE_STOPPING: + return "stopping"; case STATE_INACTIVE: return "down"; case STATE_DEAD: @@ -24,102 +25,80 @@ const char* service_status_name(service_t* s) { } void service_encode(service_t* s, service_serial_t* buffer) { - buffer->pid[0] = (s->pid >> 0) & 0xff; - buffer->pid[1] = (s->pid >> 8) & 0xff; - buffer->pid[2] = (s->pid >> 16) & 0xff; - buffer->pid[3] = (s->pid >> 24) & 0xff; - - buffer->status_change[0] = (s->status_change >> 0) & 0xff; - buffer->status_change[1] = (s->status_change >> 8) & 0xff; - buffer->status_change[2] = (s->status_change >> 16) & 0xff; - buffer->status_change[3] = (s->status_change >> 24) & 0xff; - buffer->status_change[4] = (s->status_change >> 32) & 0xff; - buffer->status_change[5] = (s->status_change >> 40) & 0xff; - buffer->status_change[6] = (s->status_change >> 48) & 0xff; - buffer->status_change[7] = (s->status_change >> 56) & 0xff; - - buffer->failcount[0] = (s->fail_count); - buffer->return_code[0] = (s->return_code); - - buffer->flags[0] = (s->state << 4) | - (s->restart_file << 2) | - (s->restart_manual << 0); - buffer->flags[1] = (s->last_exit << 6) | - (service_need_restart(s) << 5) | - (s->paused << 4) | - (s->is_log_service << 3) | - ((s->log_service != NULL) << 2); -} - -void service_encode_runit(service_t* s, service_serial_runit_t* buffer) { uint64_t tai = (uint64_t) s->status_change + 4611686018427387914ULL; - int runit_state; + int state_runit; + switch (s->state) { case STATE_INACTIVE: case STATE_DEAD: - runit_state = 0; + state_runit = 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; + case STATE_STOPPING: + state_runit = 1; break; case STATE_FINISHING: - case STATE_STOPPING: - runit_state = 2; + state_runit = 2; break; } - buffer->status_change[0] = (tai >> 56) & 0xff; - buffer->status_change[1] = (tai >> 48) & 0xff; - buffer->status_change[2] = (tai >> 40) & 0xff; - buffer->status_change[3] = (tai >> 32) & 0xff; - buffer->status_change[4] = (tai >> 24) & 0xff; - buffer->status_change[5] = (tai >> 16) & 0xff; - buffer->status_change[6] = (tai >> 8) & 0xff; - buffer->status_change[7] = (tai >> 0) & 0xff; - buffer->status_change_nsec[0] = 0; // not implemented - buffer->status_change_nsec[1] = 0; // not implemented - buffer->status_change_nsec[2] = 0; // not implemented - buffer->status_change_nsec[3] = 0; // not implemented - buffer->pid[0] = (s->pid >> 0) & 0xff; - buffer->pid[1] = (s->pid >> 8) & 0xff; - buffer->pid[2] = (s->pid >> 16) & 0xff; - buffer->pid[3] = (s->pid >> 24) & 0xff; - buffer->paused[0] = (s->paused); - buffer->wants_up[0] = service_need_restart(s) ? 'u' : 'd'; - buffer->terminated[0] = 0; // not implemented - buffer->state[0] = runit_state; + buffer->status_change[0] = (tai >> 56) & 0xff; + buffer->status_change[1] = (tai >> 48) & 0xff; + buffer->status_change[2] = (tai >> 40) & 0xff; + buffer->status_change[3] = (tai >> 32) & 0xff; + buffer->status_change[4] = (tai >> 24) & 0xff; + buffer->status_change[5] = (tai >> 16) & 0xff; + buffer->status_change[6] = (tai >> 8) & 0xff; + buffer->status_change[7] = (tai >> 0) & 0xff; + + buffer->state = s->state; + buffer->return_code = s->return_code; + buffer->fail_count = s->fail_count; + + buffer->flags = (s->restart_file << 4) | + (s->restart_manual << 2) | + (s->last_exit << 0); + + buffer->pid[0] = (s->pid >> 0) & 0xff; + buffer->pid[1] = (s->pid >> 8) & 0xff; + buffer->pid[2] = (s->pid >> 16) & 0xff; + buffer->pid[3] = (s->pid >> 24) & 0xff; + + buffer->paused = s->paused; + buffer->restart = service_need_restart(s); + buffer->force_down = s->restart_manual == S_FORCE_DOWN; + buffer->state_runit = state_runit; } + void service_decode(service_t* s, const service_serial_t* buffer) { - s->pid = (buffer->pid[3] << 24) | - (buffer->pid[2] << 16) | - (buffer->pid[1] << 8) | - (buffer->pid[0]); + 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 = ((uint64_t) buffer->status_change[7] << 56) | - ((uint64_t) buffer->status_change[6] << 48) | - ((uint64_t) buffer->status_change[5] << 40) | - ((uint64_t) buffer->status_change[4] << 32) | - ((uint64_t) buffer->status_change[3] << 24) | - ((uint64_t) buffer->status_change[2] << 16) | - ((uint64_t) buffer->status_change[1] << 8) | - ((uint64_t) buffer->status_change[0]); + s->status_change = tai - 4611686018427387914ULL; - s->fail_count = buffer->failcount[0]; - s->return_code = buffer->return_code[0]; + s->state = buffer->state; + s->return_code = buffer->return_code; + s->fail_count = buffer->fail_count; + s->restart_file = (buffer->flags >> 4) & 0x03; + s->restart_manual = (buffer->flags >> 2) & 0x03; + s->last_exit = (buffer->flags >> 0) & 0x03; - s->state = (buffer->flags[0] >> 6) & 0x0F; - s->restart_file = (buffer->flags[0] >> 4) & 0x03; - s->restart_manual = (buffer->flags[0] >> 2) & 0x03; + s->pid = (buffer->pid[0] << 0) | + (buffer->pid[1] << 8) | + (buffer->pid[2] << 16) | + (buffer->pid[3] << 24); - s->last_exit = (buffer->flags[1] >> 6) & 0x03; - s->restart_file = (buffer->flags[1] >> 5) & 0x01; - s->paused = (buffer->flags[1] >> 4) & 0x01; - s->is_log_service = (buffer->flags[1] >> 3) & 0x01; - s->log_service = (buffer->flags[1] >> 2) & 0x01 ? (void*) 1 : (void*) 0; + s->paused = buffer->paused; + s->restart_manual = buffer->restart; } diff --git a/src/service.c b/src/service.c @@ -12,16 +12,16 @@ #include <unistd.h> -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; -dependency_t depends[SV_DEPENDENCY_MAX]; -int depends_size; +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; +service_t* depends[SV_DEPENDENCY_MAX][2]; +int depends_size; service_t* service_get(const char* name) { for (int i = 0; i < services_size; i++) { @@ -88,8 +88,8 @@ int service_refresh_directory(void) { static bool is_dependency(service_t* d) { service_t* s; for (int i = 0; i < depends_size; i++) { - s = depends[i].service; - if (depends[i].depends == d && (s->state != STATE_INACTIVE || service_need_restart(s))) + s = depends[i][0]; + if (depends[i][1] == d && (s->state != STATE_INACTIVE || service_need_restart(s))) return true; } return false; diff --git a/src/stage.c b/src/stage.c @@ -58,7 +58,6 @@ void service_stage(int stage) { _exit(1); } - int child; int sig = 0; sigemptyset(&ss); @@ -75,7 +74,7 @@ void service_stage(int stage) { reclaim_console(); - if (child == pid && stage == 0) { + if (stage == 0) { if (!WIFEXITED(exitstat) || WEXITSTATUS(exitstat) != 0) { if (WIFSIGNALED(exitstat)) { /* this is stage 1 */ diff --git a/src/start.c b/src/start.c @@ -131,22 +131,14 @@ void service_run(service_t* s) { service_update_status(s); } -void service_start(service_t* s, bool* changed) { +void service_start(service_t* s) { if (s->state != STATE_INACTIVE) return; - if (changed) - *changed = true; - printf("starting %s\n", s->name); for (int i = 0; i < depends_size; i++) { - if (depends[i].service == s) - service_start(depends[i].depends, NULL); - } - - for (int i = 0; i < depends_size; i++) { - if (depends[i].service == s) - service_start(depends[i].depends, NULL); + if (depends[i][0] == s) + service_start(depends[i][1]); } struct stat st; diff --git a/src/stop.c b/src/stop.c @@ -9,22 +9,17 @@ #include <unistd.h> -void service_stop(service_t* s, bool* changed) { +void service_stop(service_t* s) { switch (s->state) { case STATE_ACTIVE_DUMMY: service_handle_exit(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: case STATE_SETUP: kill(s->pid, SIGTERM); - if (changed) - *changed = true; s->status_change = time(NULL); service_update_status(s); @@ -35,8 +30,6 @@ void service_stop(service_t* s, bool* changed) { print_error("error: cannot execute ./stop: %s\n"); s->state = STATE_INACTIVE; } - if (changed) - *changed = true; s->status_change = time(NULL); service_update_status(s); @@ -45,8 +38,6 @@ void service_stop(service_t* s, bool* changed) { case STATE_STOPPING: case STATE_FINISHING: kill(s->pid, SIGTERM); - if (changed) - *changed = true; s->status_change = time(NULL); service_update_status(s); @@ -61,6 +52,6 @@ void service_kill(service_t* s, int signal) { if (!s->pid) return; - if (s->state == STATE_ACTIVE_FOREGROUND || s->state == STATE_ACTIVE_PID) + if (s->state == STATE_ACTIVE_FOREGROUND) kill(s->pid, signal); } diff --git a/src/supervise.c b/src/supervise.c @@ -44,17 +44,6 @@ static void signal_child(int unused) { service_handle_exit(s, WIFSIGNALED(status), WIFSIGNALED(status) ? WTERMSIG(status) : WEXITSTATUS(status)); } -static void check_deaths(void) { - service_t* s; - for (int i = 0; i < services_size; i++) { - s = &services[i]; - if (s->state == STATE_ACTIVE_PID) { - if (kill(s->pid, 0) == -1 && errno == ESRCH) - service_handle_exit(s, false, 0); - } - } -} - static void check_services(void) { service_t* s; for (int i = 0; i < services_size; i++) { @@ -63,13 +52,13 @@ static void check_services(void) { continue; if (service_need_restart(s)) { if (s->state == STATE_INACTIVE) { - service_start(s, NULL); + service_start(s); s->status_change = time(NULL); service_update_status(s); } } else { if (s->state != STATE_INACTIVE) { - service_stop(s, NULL); + service_stop(s); s->status_change = time(NULL); service_update_status(s); } @@ -77,31 +66,26 @@ static void check_services(void) { } } -static void accept_socket(void) { - int client_fd; - if ((client_fd = accept(control_socket, NULL, NULL)) == -1) { - if (errno == EWOULDBLOCK) { - sleep(SV_ACCEPT_INTERVAL); - } else { - print_error("error: cannot accept client from control-socket: %s\n"); - } - } else { - service_handle_client(client_fd); - } -} static void control_sockets(void) { -#if SV_RUNIT_COMPAT != 0 service_t* s; - char cmd; + char cmd, chr; + bool read_signo = false; for (int i = 0; i < services_size; i++) { s = &services[i]; - - while (recv(s->control, &cmd, 1, MSG_DONTWAIT) == 1) { - service_handle_command_runit(s, cmd); + 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); + } } } -#endif } int service_supervise(const char* service_dir_, const char* runlevel_, bool force_socket) { @@ -120,81 +104,28 @@ int service_supervise(const char* service_dir_, const char* runlevel_, bool forc // setenv("SERVICE_RUNLEVEL", runlevel, true); - umask(0002); - - char socket_path[PATH_MAX]; - snprintf(socket_path, PATH_MAX, SV_CONTROL_SOCKET, runlevel); - if ((null_fd = open("/dev/null", O_RDWR)) == -1) { print_error("error: cannot open /dev/null: %s\n"); null_fd = 1; } - struct stat socket_stat; - if (force_socket) { - if (unlink(socket_path) == -1 && errno != ENOENT) { - print_error("error: cannot unlink socket: %s\n"); - } - } else if (stat(socket_path, &socket_stat) != -1 && S_ISREG(socket_stat.st_mode)) { - printf("error: %s exist and is locking supervision,\nrun this program with '-f' flag if you are sure no other superviser is running.", socket_path); - return 1; - } - // create socket - if ((control_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - print_error("error: cannot create socket: %s\n"); - return 1; - } - - // bind socket to address - struct sockaddr_un addr = { 0 }; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)); - if (bind(control_socket, (struct sockaddr*) &addr, sizeof(addr)) == -1) { - print_error("error: cannot bind %s to socket: %s\n", socket_path); - return 1; - } - - // listen for connections - if (listen(control_socket, 5) == -1) { - print_error("error: cannot listen to control socket: %s\n"); - return 1; - } - - int sockflags = fcntl(control_socket, F_GETFL, 0); - if (sockflags == -1) { - print_error("warn: fcntl-getflags on control-socket failed: %s\n"); - } else if (fcntl(control_socket, F_SETFL, sockflags | O_NONBLOCK) == -1) { - print_error("warn: fcntl-setflags on control-socket failed: %s\n"); - } - printf(":: starting services on '%s'\n", runlevel); - if (service_refresh_directory() < 0) - return 1; - - printf(":: started services\n"); - // accept connections and handle requests while (daemon_running) { - check_deaths(); service_refresh_directory(); check_services(); control_sockets(); - accept_socket(); + sleep(1); } - close(control_socket); - - if (unlink(socket_path) == -1 && errno != ENOENT) { - print_error("error: cannot unlink socket: %s\n"); - } printf(":: terminating\n"); service_t* s; for (int i = 0; i < services_size; i++) { s = &services[i]; - service_stop(s, NULL); + service_stop(s); } time_t start = time(NULL); diff --git a/src/util.c b/src/util.c @@ -3,9 +3,10 @@ #include <errno.h> #include <fcntl.h> #include <limits.h> +#include <signal.h> #include <stdarg.h> +#include <stdlib.h> #include <string.h> -#include <signal.h> #include <sys/stat.h> #include <unistd.h> @@ -96,12 +97,23 @@ void sigblock_all(int unblock) { sigset_t ss; sigemptyset(&ss); sigfillset(&ss); -/* sigaddset(&ss, SIGALRM); - sigaddset(&ss, SIGCHLD); - sigaddset(&ss, SIGCONT); - sigaddset(&ss, SIGHUP); - sigaddset(&ss, SIGINT); - sigaddset(&ss, SIGPIPE); - sigaddset(&ss, SIGTERM);*/ + /* sigaddset(&ss, SIGALRM); + sigaddset(&ss, SIGCHLD); + sigaddset(&ss, SIGCONT); + sigaddset(&ss, SIGHUP); + sigaddset(&ss, SIGINT); + sigaddset(&ss, SIGPIPE); + sigaddset(&ss, SIGTERM);*/ sigprocmask(unblock, &ss, NULL); } + + +long parse_long(const char* str, const char* name) { + char* end; + long l = strtol(str, &end, 10); + if (*end != '\0') { + fprintf(stderr, "error: invalid %s '%s'\n", name, optarg); + exit(1); + } + return l; +}