diff --git a/src/include/66/svc.h b/src/include/66/svc.h index 219dbb49eef29ebb9247af3f57f175162b81201a..348cc8670509f536fbcc87109c2b8c42d478cc9c 100644 --- a/src/include/66/svc.h +++ b/src/include/66/svc.h @@ -19,10 +19,44 @@ #include <66/ssexec.h> #include <66/graph.h> +#include <skalibs/tai.h> + +#include <66/service.h> + +#define DATASIZE 63 + +#define SVC_FLAGS_STARTING 1 // 1 starting not really up +#define SVC_FLAGS_STOPPING (1 << 1) // 2 stopping not really down +#define SVC_FLAGS_UP (1 << 2) // 4 really up +#define SVC_FLAGS_DOWN (1 << 3) // 8 really down +#define SVC_FLAGS_BLOCK (1 << 4) // 16 all deps are not up/down +#define SVC_FLAGS_UNBLOCK (1 << 5) // 32 all deps are up/down +#define SVC_FLAGS_FATAL (1 << 6) // 64 process crashed + +typedef struct pidservice_s pidservice_t, *pidservice_t_ref ; +struct pidservice_s +{ + int pipe[2] ; + pid_t pid ; + int aresid ; // id at array ares + unsigned int vertex ; // id at graph_hash_t struct + uint8_t state ; + int nedge ; + unsigned int edge[SS_MAX_SERVICE + 1] ; // array of id at graph_hash_t struct + int nnotif ; + /** id at graph_hash_t struct of depends/requiredby service + * to notify when a service is started/stopped */ + unsigned int notif[SS_MAX_SERVICE + 1] ; +} ; +#define PIDSERVICE_ZERO { { -1, -1 }, -1, -1, 0, 0, 0, { 0 } } + +extern int svc_launch(pidservice_t *apids, unsigned int len, uint8_t what, graph_t *graph, resolve_service_t *ares, uint8_t timeout, ssexec_t *info, char const *rise, uint8_t rise_opt, uint8_t msg, char const *signal, uint8_t propagate) ; +extern int svc_compute_ns(resolve_service_t *res, uint8_t what, uint8_t timeout, ssexec_t *info, char const *updown, uint8_t opt_updown, uint8_t reloadmsg,char const *data, uint8_t propagate) ; extern int svc_scandir_ok (char const *dir) ; extern int svc_scandir_send(char const *scandir,char const *signal) ; extern int svc_send(char const *const *list, unsigned int nservice, char **sig, unsigned int siglen, ssexec_t *info) ; extern int svc_send_wait(char const *const *list, unsigned int nservice, char **sig, unsigned int siglen, ssexec_t *info) ; extern void svc_unsupervise(unsigned int *alist, unsigned int alen, graph_t *g, resolve_service_t *ares, unsigned int areslen) ; +extern void svc_send_fdholder(char const *socket) ; #endif diff --git a/src/lib66/exec/ssexec_signal.c b/src/lib66/exec/ssexec_signal.c index 98100fc1867d02421c741bf92b97d1c8ba9db29f..36f654d84715c56acc40e0986de9ef0dff50191b 100644 --- a/src/lib66/exec/ssexec_signal.c +++ b/src/lib66/exec/ssexec_signal.c @@ -12,142 +12,24 @@ * except according to the terms contained in the LICENSE file./ */ - #include <string.h> -#include <errno.h> -#include <sys/wait.h> -#include <signal.h> -#include <unistd.h>//access -#include <stdlib.h>//malloc, free +#include <stdint.h> -#include <oblibs/obgetopt.h> #include <oblibs/log.h> -#include <oblibs/types.h> -#include <oblibs/string.h> -#include <oblibs/directory.h> #include <oblibs/graph.h> -#include <oblibs/sastr.h> -#include <oblibs/environ.h> - -#include <skalibs/types.h> -#include <skalibs/stralloc.h> -#include <skalibs/genalloc.h> -#include <skalibs/djbunix.h> -#include <skalibs/bytestr.h> -#include <skalibs/selfpipe.h> -#include <skalibs/iopause.h> -#include <skalibs/tai.h> -#include <skalibs/types.h> -#include <skalibs/sig.h>//sig_ignore +#include <oblibs/types.h> -#include <s6/supervise.h>//s6_svstatus_t -#include <s6/ftrigr.h> -#include <s6/ftrigw.h> +#include <skalibs/sgetopt.h> -#include <66/utils.h> -#include <66/constants.h> #include <66/svc.h> -#include <66/ssexec.h> +#include <66/config.h> #include <66/resolve.h> -#include <66/state.h> +#include <66/ssexec.h> #include <66/service.h> +#include <66/state.h> #include <66/enum.h> -#include <66/graph.h> - -#define FLAGS_STARTING 1 // 1 starting not really up -#define FLAGS_STOPPING (1 << 1) // 2 stopping not really down -#define FLAGS_UP (1 << 2) // 4 really up -#define FLAGS_DOWN (1 << 3) // 8 really down -#define FLAGS_BLOCK (1 << 4) // 16 all deps are not up/down -#define FLAGS_UNBLOCK (1 << 5) // 32 all deps are up/down -#define FLAGS_FATAL (1 << 6) // 64 process crashed - -#define DATASIZE 63 - -static unsigned int napid = 0 ; -static unsigned int npid = 0 ; - -static resolve_service_t_ref pares = 0 ; -static unsigned int *pareslen = 0 ; -static char updown[4] = "-w \0" ; -static uint8_t opt_updown = 0 ; -static char data[DATASIZE + 1] = "-" ; -static unsigned int datalen = 1 ; -static uint8_t reloadmsg = 0 ; - -typedef struct pidservice_s pidservice_t, *pidservice_t_ref ; -struct pidservice_s -{ - int pipe[2] ; - pid_t pid ; - int aresid ; // id at array ares - unsigned int vertex ; // id at graph_hash_t struct - uint8_t state ; - int nedge ; - unsigned int edge[SS_MAX_SERVICE + 1] ; // array of id at graph_hash_t struct - int nnotif ; - /** id at graph_hash_t struct of depends/requiredby service - * to notify when a service is started/stopped */ - unsigned int notif[SS_MAX_SERVICE + 1] ; -} ; -#define PIDSERVICE_ZERO { { -1, -1 }, -1, -1, 0, 0, 0, { 0 } } - -typedef enum fifo_e fifo_t, *fifo_t_ref ; -enum fifo_e -{ - FIFO_u = 0, - FIFO_U, - FIFO_d, - FIFO_D, - FIFO_F, - FIFO_b, - FIFO_B -} ; - -typedef enum service_action_e service_action_t, *service_action_t_ref ; -enum service_action_e -{ - SERVICE_ACTION_GOTIT = 0, - SERVICE_ACTION_WAIT, - SERVICE_ACTION_FATAL, - SERVICE_ACTION_UNKNOWN -} ; - -static const unsigned char actions[2][7] = { - // u U d D F b B - { SERVICE_ACTION_WAIT, SERVICE_ACTION_GOTIT, SERVICE_ACTION_UNKNOWN, SERVICE_ACTION_UNKNOWN, SERVICE_ACTION_FATAL, SERVICE_ACTION_WAIT, SERVICE_ACTION_WAIT }, // !what -> up - { SERVICE_ACTION_UNKNOWN, SERVICE_ACTION_UNKNOWN, SERVICE_ACTION_WAIT, SERVICE_ACTION_GOTIT, SERVICE_ACTION_FATAL, SERVICE_ACTION_WAIT, SERVICE_ACTION_WAIT } // what -> down - -} ; - -// convert signal into enum number -static const unsigned int char2enum[128] = -{ - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //8 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //16 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //24 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //32 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //40 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //48 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //56 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //64 - 0 , 0 , FIFO_B , 0 , FIFO_D , 0 , FIFO_F , 0 , //72 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //80 - 0 , 0 , 0 , 0 , 0 , FIFO_U, 0 , 0 , //88 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //96 - 0 , 0 , FIFO_b , 0 , FIFO_d , 0 , 0 , 0 , //104 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //112 - 0 , 0 , 0 , 0 , 0 , FIFO_u , 0 , 0 , //120 - 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 //128 -} ; - -static inline void kill_all(pidservice_t *apids) -{ - log_flow() ; - unsigned int j = napid ; - while (j--) kill(apids[j].pid, SIGKILL) ; -} +#include <s6/supervise.h>//s6_svstatus_t static pidservice_t pidservice_init(unsigned int len) { @@ -163,119 +45,6 @@ static pidservice_t pidservice_init(unsigned int len) return pids ; } -static int pidservice_get_id(pidservice_t *apids, unsigned int id) -{ - log_flow() ; - - unsigned int pos = 0 ; - - for (; pos < napid ; pos++) { - if (apids[pos].vertex == id) - return (unsigned int) pos ; - } - return -1 ; -} - -static void notify(pidservice_t *apids, unsigned int pos, char const *sig, unsigned int what) -{ - log_flow() ; - - unsigned int i = 0, idx = 0 ; - char fmt[UINT_FMT] ; - uint8_t flag = what ? FLAGS_DOWN : FLAGS_UP ; - - for (; i < apids[pos].nnotif ; i++) { - - for (idx = 0 ; idx < napid ; idx++) { - - if (apids[pos].notif[i] == apids[idx].vertex && !FLAGS_ISSET(apids[idx].state, flag)) { - - size_t nlen = uint_fmt(fmt, apids[pos].aresid) ; - fmt[nlen] = 0 ; - size_t len = nlen + 1 + 2 ; - char s[len + 1] ; - auto_strings(s, fmt, ":", sig, "@") ; - - log_trace("sends notification ", sig, " to: ", pares[apids[idx].aresid].sa.s + pares[apids[idx].aresid].name, " from: ", pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].name) ; - - if (write(apids[idx].pipe[1], s, strlen(s)) < 0) - log_dieusys(LOG_EXIT_SYS, "send notif to: ", pares[apids[idx].aresid].sa.s + pares[apids[idx].aresid].name) ; - } - } - } -} - -/** - * @what: up or down - * @success: 0 fail, 1 win - * */ -static void announce(unsigned int pos, pidservice_t *apids, unsigned int what, unsigned int success, unsigned int exitcode) -{ - log_flow() ; - - int fd = 0 ; - char fmt[UINT_FMT] ; - char const *name = pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].name ; - char const *base = pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].path.home ; - char const *scandir = pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].live.scandir ; - size_t scandirlen = strlen(scandir) ; - char file[scandirlen + 6] ; - - auto_strings(file, scandir, "/down") ; - - uint8_t flag = what ? FLAGS_DOWN : FLAGS_UP ; - - if (success) { - - if (pares[apids[pos].aresid].type == TYPE_CLASSIC) { - - fd = open_trunc(file) ; - if (fd < 0) - log_dieusys(LOG_EXIT_SYS, "create file: ", scandir) ; - fd_close(fd) ; - } - - notify(apids, pos, "F", what) ; - - fmt[uint_fmt(fmt, exitcode)] = 0 ; - - log_1_warn("Unable to ", reloadmsg == 1 ? "restart" : reloadmsg > 1 ? "reload" : what ? "stop" : "start", " service: ", name, " -- exited with signal: ", fmt) ; - - FLAGS_SET(apids[pos].state, FLAGS_BLOCK|FLAGS_FATAL) ; - - } else { - - if (!state_messenger(base, name, STATE_FLAGS_ISUP, what ? STATE_FLAGS_FALSE : STATE_FLAGS_TRUE)) - log_dieusys(LOG_EXIT_SYS, "send message to state of: ", name) ; - - if (!pares[apids[pos].aresid].execute.down && pares[apids[pos].aresid].type == TYPE_CLASSIC) { - - if (!what) { - - if (!access(scandir, F_OK)) { - log_trace("delete down file: ", file) ; - if (unlink(file) < 0 && errno != ENOENT) - log_warnusys("delete down file: ", file) ; - } - - } else { - - fd = open_trunc(file) ; - if (fd < 0) - log_dieusys(LOG_EXIT_SYS, "create file: ", file) ; - fd_close(fd) ; - } - } - - notify(apids, pos, what ? "D" : "U", what) ; - - FLAGS_CLEAR(apids[pos].state, FLAGS_BLOCK) ; - FLAGS_SET(apids[pos].state, flag|FLAGS_UNBLOCK) ; - - log_info("Successfully ", reloadmsg == 1 ? "restarted" : reloadmsg > 1 ? "reloaded" : what ? "stopped" : "started", " service: ", name) ; - } -} - static void pidservice_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apids, graph_t *g, resolve_service_t *ares, unsigned int areslen, ssexec_t *info, uint8_t requiredby, uint32_t flag) { log_flow() ; @@ -317,13 +86,13 @@ static void pidservice_init_array(unsigned int *list, unsigned int listlen, pids ss_state_t ste = STATE_ZERO ; - if (!state_read(&ste, ares[pids.aresid].sa.s + ares[pids.aresid].path.home, name)) + if (!state_read(&ste, &ares[pids.aresid])) log_dieusys(LOG_EXIT_SYS, "read state file of: ", name) ; if (ste.isup == STATE_FLAGS_TRUE) - FLAGS_SET(pids.state, FLAGS_UP) ; + FLAGS_SET(pids.state, SVC_FLAGS_UP) ; else - FLAGS_SET(pids.state, FLAGS_DOWN) ; + FLAGS_SET(pids.state, SVC_FLAGS_DOWN) ; } else { @@ -335,10 +104,10 @@ static void pidservice_init_array(unsigned int *list, unsigned int listlen, pids if (pid > 0) { - FLAGS_SET(pids.state, FLAGS_UP) ; + FLAGS_SET(pids.state, SVC_FLAGS_UP) ; } else - FLAGS_SET(pids.state, FLAGS_DOWN) ; + FLAGS_SET(pids.state, SVC_FLAGS_DOWN) ; } apids[pos] = pids ; @@ -346,471 +115,22 @@ static void pidservice_init_array(unsigned int *list, unsigned int listlen, pids } -static int handle_signal(pidservice_t *apids, unsigned int what, graph_t *graph, ssexec_t *info) -{ - log_flow() ; - - int ok = 0 ; - - for (;;) { - - int s = selfpipe_read() ; - switch (s) { - - case -1 : log_dieusys(LOG_EXIT_SYS,"selfpipe_read") ; - case 0 : return ok ; - case SIGCHLD : - - for (;;) { - - unsigned int pos = 0 ; - int wstat ; - pid_t r = wait_nohang(&wstat) ; - - if (r < 0) { - - if (errno = ECHILD) - break ; - else - log_dieusys(LOG_EXIT_SYS,"wait for children") ; - - } else if (!r) break ; - - for (; pos < napid ; pos++) - if (apids[pos].pid == r) - break ; - - if (pos < napid) { - - if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) { - - announce(pos, apids, what, 0, 0) ; - - } else { - - ok = WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat) ; - announce(pos, apids, what, 1, ok) ; - - kill_all(apids) ; - break ; - } - - npid-- ; - } - } - break ; - case SIGTERM : - case SIGKILL : - case SIGINT : - log_1_warn("received SIGINT, aborting transaction") ; - kill_all(apids) ; - ok = 111 ; - break ; - default : log_die(LOG_EXIT_SYS, "unexpected data in selfpipe") ; - } - } - - return ok ; -} - -/** this following function come from: - * https://git.skarnet.org/cgi-bin/cgit.cgi/s6-rc/tree/src/s6-rc/s6-rc.c#n111 - * under license ISC where parameters was modified */ -static uint32_t compute_timeout (uint32_t timeout, tain *deadline) -{ - uint32_t t = timeout ; - int globalt ; - tain globaltto ; - tain_sub(&globaltto, deadline, &STAMP) ; - globalt = tain_to_millisecs(&globaltto) ; - if (!globalt) globalt = 1 ; - if (globalt > 0 && (!t || (unsigned int)globalt < t)) - t = (uint32_t)globalt ; - return t ; -} - -static int doit(pidservice_t *sv, unsigned int what, tain *deadline) -{ - log_flow() ; - - uint8_t type = pares[sv->aresid].type ; - - pid_t pid ; - int wstat ; - - char tfmt[UINT32_FMT] ; - - unsigned int timeout = 0 ; - if (!what) - timeout = compute_timeout(type == TYPE_ONESHOT ? pares[sv->aresid].execute.timeout.up : pares[sv->aresid].execute.timeout.kill, deadline) ; - else - timeout = compute_timeout(type == TYPE_ONESHOT ? pares[sv->aresid].execute.timeout.down : pares[sv->aresid].execute.timeout.finish, deadline) ; - - tfmt[uint_fmt(tfmt, timeout)] = 0 ; - - if (type == TYPE_MODULE || type == TYPE_BUNDLE) - /** - * Those type are not real services. Passing here with - * this kind of service means that the dependencies - * of the service was passed. So, we can consider it as - * already up/down. - * */ - return 0 ; - - if (type == TYPE_CLASSIC) { - - char *scandir = pares[sv->aresid].sa.s + pares[sv->aresid].live.scandir ; - - if (updown[2] == 'U' || updown[2] == 'D' || updown[2] == 'R') { - - if (!pares[sv->aresid].notify) - updown[2] = updown[2] == 'U' ? 'u' : updown[2] == 'D' ? 'd' : updown[2] == 'R' ? 'r' : updown[2] ; - - } - - char const *newargv[8] ; - unsigned int m = 0 ; - - newargv[m++] = "s6-svc" ; - newargv[m++] = data ; - - if (opt_updown) - newargv[m++] = updown ; - - newargv[m++] = "-T" ; - newargv[m++] = tfmt ; - newargv[m++] = "--" ; - newargv[m++] = scandir ; - newargv[m++] = 0 ; - - log_trace("sending ", opt_updown ? newargv[2] : "", opt_updown ? " " : "", data, " to: ", scandir) ; - - pid = child_spawn0(newargv[0], newargv, (char const *const *) environ) ; - - if (waitpid_nointr(pid, &wstat, 0) < 0) - log_warnusys_return(LOG_EXIT_ZERO, "wait for s6-svc") ; - - if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) - return WEXITSTATUS(wstat) ; - else - return WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat) ; - - } - - char *sa = pares[sv->aresid].sa.s ; - char *name = sa + pares[sv->aresid].name ; - size_t namelen = strlen(name) ; - char *home = pares[sv->aresid].sa.s + pares[sv->aresid].path.home ; - size_t homelen = strlen(home) ; - - char script[homelen + SS_SYSTEM_LEN + SS_SERVICE_LEN + SS_SVC_LEN + 1 + namelen + 7 + 1] ; - auto_strings(script, home, SS_SYSTEM, SS_SERVICE, SS_SVC, "/", name) ; - - char *oneshotdir = pares[sv->aresid].sa.s + pares[sv->aresid].live.oneshotddir ; - char *scandir = pares[sv->aresid].sa.s + pares[sv->aresid].live.scandir ; - char oneshot[strlen(oneshotdir) + 2 + 1] ; - auto_strings(oneshot, oneshotdir, "/s") ; - - char const *newargv[11] ; - unsigned int m = 0 ; - newargv[m++] = "s6-sudo" ; - newargv[m++] = VERBOSITY >= 4 ? "-vel0" : "-el0" ; - newargv[m++] = "-t" ; - newargv[m++] = "30000" ; - newargv[m++] = "-T" ; - newargv[m++] = tfmt ; - newargv[m++] = "--" ; - newargv[m++] = oneshot ; - newargv[m++] = !what ? "up" : "down" ; - newargv[m++] = script ; - newargv[m++] = 0 ; - - log_trace("sending ", !what ? "up" : "down", " to: ", scandir) ; - - pid = child_spawn0(newargv[0], newargv, (char const *const *) environ) ; - - if (waitpid_nointr(pid, &wstat, 0) < 0) - log_warnusys_return(LOG_EXIT_ZERO, "wait for s6-sudo") ; - - if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) - return WEXITSTATUS(wstat) ; - else - return WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat) ; -} - -static int check_action(pidservice_t *apids, unsigned int pos, unsigned int receive, unsigned int what) -{ - unsigned int p = char2enum[receive] ; - unsigned char action = actions[what][p] ; - - switch(action) { - - case SERVICE_ACTION_GOTIT: - FLAGS_SET(apids[pos].state, (!what ? FLAGS_UP : FLAGS_DOWN)) ; - return 1 ; - - case SERVICE_ACTION_FATAL: - FLAGS_SET(apids[pos].state, FLAGS_FATAL) ; - return -1 ; - - case SERVICE_ACTION_WAIT: - return 0 ; - - case SERVICE_ACTION_UNKNOWN: - default: - log_die(LOG_EXIT_ZERO,"invalid action -- please make a bug report") ; - } - -} - -static int async_deps(pidservice_t *apids, unsigned int i, unsigned int what, ssexec_t *info, graph_t *graph, tain *deadline) -{ - log_flow() ; - - int r ; - unsigned int pos = 0, id = 0, ilog = 0, idx = 0 ; - char buf[(UINT_FMT*2)*SS_MAX_SERVICE + 1] ; - - tain dead ; - tain_now_set_stopwatch_g() ; - tain_add_g(&dead, deadline) ; - - iopause_fd x = { .fd = apids[i].pipe[0], .events = IOPAUSE_READ, 0 } ; - - unsigned int n = apids[i].nedge ; - unsigned int visit[n] ; - - graph_array_init_single(visit, n) ; - - while (pos < n) { - - r = iopause_g(&x, 1, &dead) ; - - if (r < 0) - log_dieusys(LOG_EXIT_SYS, "iopause") ; - - if (!r) { - errno = ETIMEDOUT ; - log_dieusys(LOG_EXIT_SYS,"time out", pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name) ; - } - - if (x.revents & IOPAUSE_READ) { - - memset(buf, 0, sizeof(buf)) ; - r = read(apids[i].pipe[0], buf, sizeof(buf)) ; - if (r < 0) - log_dieu(LOG_EXIT_SYS, "read from pipe") ; - buf[r] = 0 ; - - idx = 0 ; - - while (r != -1) { - /** The buf might contain multiple signal coming - * from the dependencies if they finished before - * the start of this read process. Check every - * signal received.*/ - r = get_len_until(buf + idx, '@') ; - - if (r < 0) - /* no more signal */ - goto next ; - - char line[r + 1] ; - memcpy(line, buf + idx, r) ; - line[r] = 0 ; - - idx += r + 1 ; - - /** - * the received string have the format: - * index_of_the_ares_array_of_the_service_dependency:signal_receive - * - * typically: - * - 10:D - * - 30:u - * - ... - * - * Split it and check the signal receive.*/ - int sep = get_len_until(line, ':') ; - if (sep < 0) - log_die(LOG_EXIT_SYS, "received bad signal format -- please make a bug report") ; - - unsigned int c = line[sep + 1] ; - char pc[2] = { c, 0 } ; - line[sep] = 0 ; - - if (!uint0_scan(line, &id)) - log_dieusys(LOG_EXIT_SYS, "retrieve service number -- please make a bug report") ; - - ilog = id ; - - log_trace(pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name, " acknowledges: ", pc, " from: ", pares[ilog].sa.s + pares[ilog].name) ; - - if (!visit[pos]) { - - id = pidservice_get_id(apids, id) ; - if (id < 0) - log_dieu(LOG_EXIT_SYS, "get apidservice id -- please make a bug report") ; - - id = check_action(apids, id, c, what) ; - if (id < 0) - log_die(LOG_EXIT_SYS, "service dependency: ", pares[ilog].sa.s + pares[ilog].name, " of: ", pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name," crashed") ; - - if (!id) - continue ; - - visit[pos++]++ ; - } - } - } - next: - - } - - return 1 ; -} - -static int async(pidservice_t *apids, unsigned int i, unsigned int what, ssexec_t *info, graph_t *graph, tain *deadline) -{ - log_flow() ; - - int e = 0 ; - - char *name = graph->data.s + genalloc_s(graph_hash_t,&graph->hash)[apids[i].vertex].vertex ; - - log_trace("beginning of the process of: ", name) ; - - if (FLAGS_ISSET(apids[i].state, (!what ? FLAGS_DOWN : FLAGS_UP))) { - - if (!FLAGS_ISSET(apids[i].state, FLAGS_BLOCK)) { - - FLAGS_SET(apids[i].state, FLAGS_BLOCK) ; - - if (apids[i].nedge) - if (!async_deps(apids, i, what, info, graph, deadline)) - log_warnu_return(LOG_EXIT_ZERO, !what ? "start" : "stop", " dependencies of service: ", name) ; - - e = doit(&apids[i], what, deadline) ; - - } else { - - log_trace("skipping service: ", name, " -- already in ", what ? "stopping" : "starting", " process") ; - - notify(apids, i, what ? "d" : "u", what) ; - - } - - } else { - - /** do not notify here, the handle will make it for us */ - log_trace("skipping service: ", name, " -- already ", what ? "down" : "up") ; - - } - - return e ; -} - -static int waitit(pidservice_t *apids, unsigned int what, graph_t *graph, tain *deadline, ssexec_t *info) -{ - log_flow() ; - - unsigned int e = 0, pos = 0 ; - int r ; - pid_t pid ; - pidservice_t apidservicetable[napid] ; - pidservice_t_ref apidservice = apidservicetable ; - - tain_now_set_stopwatch_g() ; - tain_add_g(deadline, deadline) ; - - int spfd = selfpipe_init() ; - - if (spfd < 0) - log_dieusys(LOG_EXIT_SYS, "selfpipe_init") ; - - if (!selfpipe_trap(SIGCHLD) || - !selfpipe_trap(SIGINT) || - !selfpipe_trap(SIGKILL) || - !selfpipe_trap(SIGTERM) || - !sig_altignore(SIGPIPE)) - log_dieusys(LOG_EXIT_SYS, "selfpipe_trap") ; - - iopause_fd x = { .fd = spfd, .events = IOPAUSE_READ, .revents = 0 } ; - - for (; pos < napid ; pos++) { - - apidservice[pos] = apids[pos] ; - - if (pipe(apidservice[pos].pipe) < 0) - log_dieusys(LOG_EXIT_SYS, "pipe"); - - } - - for (pos = 0 ; pos < napid ; pos++) { - - pid = fork() ; - - if (pid < 0) - log_dieusys(LOG_EXIT_SYS, "fork") ; - - if (!pid) { - - selfpipe_finish() ; - - close(apidservice[pos].pipe[1]) ; - - e = async(apidservice, pos, what, info, graph, deadline) ; - - goto end ; - } - - apidservice[pos].pid = pid ; - - close(apidservice[pos].pipe[0]) ; - - npid++ ; - } - - while (npid) { - - r = iopause_g(&x, 1, deadline) ; - - if (r < 0) - log_dieusys(LOG_EXIT_SYS, "iopause") ; - - if (!r) { - errno = ETIMEDOUT ; - log_diesys(LOG_EXIT_SYS,"time out") ; - } - - if (x.revents & IOPAUSE_READ) { - e = handle_signal(apidservice, what, graph, info) ; - - if (e) - break ; - } - } - - - selfpipe_finish() ; - end: - for (pos = 0 ; pos < napid ; pos++) { - close(apidservice[pos].pipe[1]) ; - close(apidservice[pos].pipe[0]) ; - } - - return e ; -} - int ssexec_signal(int argc, char const *const *argv, ssexec_t *info) { log_flow() ; int r ; - uint8_t what = 0, requiredby = 0 ; - tain deadline ; + uint8_t what = 0, requiredby = 0, propagate = 1 ; graph_t graph = GRAPH_ZERO ; + unsigned int napid = 0 ; + + char updown[4] = "-w \0" ; + uint8_t opt_updown = 0 ; + char data[DATASIZE + 1] = "-" ; + unsigned int datalen = 1 ; + uint8_t reloadmsg = 0 ; + unsigned int areslen = 0, list[SS_MAX_SERVICE], visit[SS_MAX_SERVICE] ; resolve_service_t ares[SS_MAX_SERVICE] ; @@ -879,6 +199,7 @@ int ssexec_signal(int argc, char const *const *argv, ssexec_t *info) break ; case 'P': + propagate = 0 ; FLAGS_CLEAR(gflag, STATE_FLAGS_TOPROPAGATE) ; break ; @@ -892,11 +213,6 @@ int ssexec_signal(int argc, char const *const *argv, ssexec_t *info) if (argc < 1 || datalen < 2) log_usage(info->usage, "\n", info->help) ; - if (info->timeout) - tain_from_millisecs(&deadline, info->timeout) ; - else - deadline = tain_infinite_relative ; - if (data[1] != 'u') what = 1 ; @@ -928,64 +244,14 @@ int ssexec_signal(int argc, char const *const *argv, ssexec_t *info) if (aresid < 0) log_die(LOG_EXIT_USER, "service: ", *argv, " not available -- did you parsed it?") ; - unsigned int l[graph.mlen], c = 0, pos = 0, idx = 0 ; - - idx = graph_hash_vertex_get_id(&graph, *argv) ; - - if (!visit[idx]) { - /** avoid double entry */ - list[napid++] = idx ; - visit[idx] = 1 ; - - } - - /** find dependencies of the service from the graph, do it recursively */ - c = graph_matrix_get_edge_g_sorted_list(l, &graph, *argv, requiredby, 1) ; - - /** append to the list to deal with */ - for (; pos < c ; pos++) { - if (!visit[l[pos]]) { - list[napid++] = l[pos] ; - visit[l[pos]] = 1 ; - } - } - if (ares[aresid].type == TYPE_MODULE) { - - if (ares[aresid].regex.ncontents) { - - stralloc sa = STRALLOC_ZERO ; - if (!sastr_clean_string(&sa, ares[aresid].sa.s + ares[aresid].regex.contents)) - log_dieu(LOG_EXIT_SYS, "clean string") ; - - { - size_t idx = 0 ; - FOREACH_SASTR(&sa, idx) { - - /** find dependencies of the service from the graph, do it recursively */ - c = graph_matrix_get_edge_g_list(l, &graph, sa.s + idx, 0, 1) ; - - /** append to the list to deal with */ - for (pos = 0 ; pos < c ; pos++) { - if (!visit[l[pos]]) { - list[napid++] = l[pos] ; - visit[l[pos]] = 1 ; - } - } - } - } - stralloc_free(&sa) ; - } - } + graph_compute_visit(*argv, visit, list, &graph, &napid, requiredby) ; } pidservice_t apids[napid] ; - pares = ares ; - pareslen = &areslen ; - pidservice_init_array(list, napid, apids, &graph, ares, areslen, info, requiredby, gflag) ; - r = waitit(apids, what, &graph, &deadline, info) ; + r = svc_launch(apids, napid, what, &graph, ares, info->timeout ? info->timeout : 0, info, updown, opt_updown, reloadmsg, data, propagate) ; graph_free_all(&graph) ; diff --git a/src/lib66/svc/deps-lib/deps b/src/lib66/svc/deps-lib/deps index 09eaab4a21ee8ffaf08931de6bf349ac5885b423..193b082a8df7002fa30af434400f9986fc03863e 100644 --- a/src/lib66/svc/deps-lib/deps +++ b/src/lib66/svc/deps-lib/deps @@ -1,6 +1,9 @@ +svc_compute_ns.o +svc_launch.o svc_scandir_ok.o svc_scandir_send.o svc_send.o +svc_send_fdholder.o svc_send_wait.o svc_unsupervise.o -ls6 diff --git a/src/lib66/svc/svc_compute_ns.c b/src/lib66/svc/svc_compute_ns.c new file mode 100644 index 0000000000000000000000000000000000000000..9744264855ade63549a457edb0dc96eb8d47d438 --- /dev/null +++ b/src/lib66/svc/svc_compute_ns.c @@ -0,0 +1,190 @@ +/* + * ssexec_compute_ns.c + * + * Copyright (c) 2018-2021 Eric Vidal <eric@obarun.org> + * + * All rights reserved. + * + * This file is part of Obarun. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution. + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file./ + */ + +#include <stdint.h> + +#include <oblibs/log.h> +#include <oblibs/graph.h> +#include <oblibs/types.h> +#include <oblibs/sastr.h> + +#include <skalibs/genalloc.h> + +#include <66/svc.h> +#include <66/config.h> +#include <66/resolve.h> +#include <66/ssexec.h> +#include <66/state.h> +#include <66/enum.h> +#include <66/service.h> +#include <66/sanitize.h> + +#include <s6/supervise.h>//s6_svstatus_t + +static pidservice_t pidservice_init(unsigned int len) +{ + log_flow() ; + + pidservice_t pids = PIDSERVICE_ZERO ; + + if (len > SS_MAX_SERVICE) + log_die(LOG_EXIT_SYS, "too many services") ; + + graph_array_init_single(pids.edge, len) ; + + return pids ; +} + +static void pidservice_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apids, graph_t *g, resolve_service_t *ares, unsigned int areslen, ssexec_t *info, uint8_t requiredby, uint32_t flag) { + + log_flow() ; + + int r = 0 ; + unsigned int pos = 0 ; + + for (; pos < listlen ; pos++) { + + pidservice_t pids = pidservice_init(g->mlen) ; + + char *name = g->data.s + genalloc_s(graph_hash_t,&g->hash)[list[pos]].vertex ; + + pids.aresid = service_resolve_array_search(ares, areslen, name) ; + + if (pids.aresid < 0) + log_dieu(LOG_EXIT_SYS,"find ares id of: ", name, " -- please make a bug reports") ; + + if (FLAGS_ISSET(flag, STATE_FLAGS_TOPROPAGATE)) { + + pids.nedge = graph_matrix_get_edge_g_sorted_list(pids.edge, g, name, requiredby, 1) ; + + if (pids.nedge < 0) + log_dieu(LOG_EXIT_SYS,"get sorted ", requiredby ? "required by" : "dependency", " list of service: ", name) ; + + pids.nnotif = graph_matrix_get_edge_g_sorted_list(pids.notif, g, name, !requiredby, 1) ; + + if (pids.nnotif < 0) + log_dieu(LOG_EXIT_SYS,"get sorted ", !requiredby ? "required by" : "dependency", " list of service: ", name) ; + + } + + pids.vertex = graph_hash_vertex_get_id(g, name) ; + + if (pids.vertex < 0) + log_dieu(LOG_EXIT_SYS, "get vertex id -- please make a bug report") ; + + if (ares[pids.aresid].type != TYPE_CLASSIC) { + + ss_state_t ste = STATE_ZERO ; + + if (!state_read(&ste, &ares[pids.aresid])) + log_dieusys(LOG_EXIT_SYS, "read state file of: ", name) ; + + if (ste.isup == STATE_FLAGS_TRUE) + FLAGS_SET(pids.state, SVC_FLAGS_UP) ; + else + FLAGS_SET(pids.state, SVC_FLAGS_DOWN) ; + + } else { + + s6_svstatus_t status ; + + r = s6_svstatus_read(ares[pids.aresid].sa.s + ares[pids.aresid].live.scandir, &status) ; + + pid_t pid = !r ? 0 : status.pid ; + + if (pid > 0) { + + FLAGS_SET(pids.state, SVC_FLAGS_UP) ; + } + else + FLAGS_SET(pids.state, SVC_FLAGS_DOWN) ; + } + + apids[pos] = pids ; + } + +} + +int svc_compute_ns(resolve_service_t *res, uint8_t what, uint8_t timeout, ssexec_t *info, char const *updown, uint8_t opt_updown, uint8_t reloadmsg,char const *data, uint8_t propagate) +{ + log_flow() ; + + int r ; + uint8_t requiredby = 0 ; + size_t pos = 0 ; + graph_t graph = GRAPH_ZERO ; + stralloc sa = STRALLOC_ZERO ; + + unsigned int napid = 0 ; + unsigned int areslen = 0, list[SS_MAX_SERVICE], visit[SS_MAX_SERVICE] ; + resolve_service_t ares[SS_MAX_SERVICE] ; + + uint32_t gflag = STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP ; + + if (!propagate) + FLAGS_CLEAR(gflag, STATE_FLAGS_TOPROPAGATE) ; + + if (what) { + requiredby = 1 ; + FLAGS_SET(gflag, STATE_FLAGS_WANTDOWN) ; + FLAGS_CLEAR(gflag, STATE_FLAGS_WANTUP) ; + } + + if (res->dependencies.ncontents) { + + if (!sastr_clean_string(&sa, res->sa.s + res->dependencies.contents)) + log_dieu(LOG_EXIT_SYS, "clean string") ; + + } else { + log_warn("empty ns: ", res->sa.s + res->name) ; + return 0 ; + } + + /** build the graph of the ns */ + service_graph_g(sa.s, sa.len, &graph, ares, &areslen, info, gflag) ; + + if (!graph.mlen) + log_die(LOG_EXIT_USER, "services selection is not supervised -- initiate its first") ; + + graph_array_init_single(visit, SS_MAX_SERVICE) ; + + FOREACH_SASTR(&sa, pos) { + + char const *name = sa.s + pos ; + + int aresid = service_resolve_array_search(ares, areslen, name) ; + if (aresid < 0) + log_die(LOG_EXIT_USER, "service: ", name, " not available -- did you parsed it?") ; + + graph_compute_visit(name, visit, list, &graph, &napid, requiredby) ; + + } + + if (!what) + sanitize_init(list, napid, &graph, ares, areslen, STATE_FLAGS_UNKNOWN) ; + + pidservice_t apids[napid] ; + + pidservice_init_array(list, napid, apids, &graph, ares, areslen, info, requiredby, gflag) ; + + r = svc_launch(apids, napid, what, &graph, ares, info->timeout ? info->timeout : 0, info, updown, opt_updown, reloadmsg, data, propagate) ; + + graph_free_all(&graph) ; + + service_resolve_array_free(ares, areslen) ; + + stralloc_free(&sa) ; + + return r ; +} diff --git a/src/lib66/svc/svc_launch.c b/src/lib66/svc/svc_launch.c new file mode 100644 index 0000000000000000000000000000000000000000..5f94592352224ea7101037c0cf0246aef5285b37 --- /dev/null +++ b/src/lib66/svc/svc_launch.c @@ -0,0 +1,688 @@ +/* + * ssexec_signal.c + * + * Copyright (c) 2018-2021 Eric Vidal <eric@obarun.org> + * + * All rights reserved. + * + * This file is part of Obarun. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution. + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file./ + */ + +#include <string.h> +#include <stdint.h> +#include <unistd.h> // access +#include <errno.h> +#include <signal.h> +#include <sys/types.h> + +#include <oblibs/log.h> +#include <oblibs/string.h> +#include <oblibs/types.h> + +#include <skalibs/types.h> +#include <skalibs/tai.h> +#include <skalibs/selfpipe.h> +#include <skalibs/djbunix.h> +#include <skalibs/genalloc.h> +#include <skalibs/iopause.h> +#include <skalibs/sig.h>//sig_ignore + +#include <66/ssexec.h> +#include <66/constants.h> +#include <66/service.h> +#include <66/enum.h> +#include <66/state.h> +#include <66/svc.h> + +static unsigned int napid = 0 ; +static unsigned int npid = 0 ; + +static resolve_service_t_ref pares = 0 ; +static char data[DATASIZE + 1] ; +static char updown[4] ; +static uint8_t opt_updown = 0 ; +static uint8_t reloadmsg = 0 ; +static uint8_t PROPAGATE = 1 ; +static ssexec_t_ref PINFO = 0 ; + +typedef enum fifo_e fifo_t, *fifo_t_ref ; +enum fifo_e +{ + FIFO_u = 0, + FIFO_U, + FIFO_d, + FIFO_D, + FIFO_F, + FIFO_b, + FIFO_B +} ; + +typedef enum service_action_e service_action_t, *service_action_t_ref ; +enum service_action_e +{ + SERVICE_ACTION_GOTIT = 0, + SERVICE_ACTION_WAIT, + SERVICE_ACTION_FATAL, + SERVICE_ACTION_UNKNOWN +} ; + +static const unsigned char actions[2][7] = { + // u U d D F b B + { SERVICE_ACTION_WAIT, SERVICE_ACTION_GOTIT, SERVICE_ACTION_UNKNOWN, SERVICE_ACTION_UNKNOWN, SERVICE_ACTION_FATAL, SERVICE_ACTION_WAIT, SERVICE_ACTION_WAIT }, // !what -> up + { SERVICE_ACTION_UNKNOWN, SERVICE_ACTION_UNKNOWN, SERVICE_ACTION_WAIT, SERVICE_ACTION_GOTIT, SERVICE_ACTION_FATAL, SERVICE_ACTION_WAIT, SERVICE_ACTION_WAIT } // what -> down + +} ; + +// convert signal into enum number +static const unsigned int char2enum[128] = +{ + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //8 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //16 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //24 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //32 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //40 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //48 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //56 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //64 + 0 , 0 , FIFO_B , 0 , FIFO_D , 0 , FIFO_F , 0 , //72 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //80 + 0 , 0 , 0 , 0 , 0 , FIFO_U, 0 , 0 , //88 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //96 + 0 , 0 , FIFO_b , 0 , FIFO_d , 0 , 0 , 0 , //104 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , //112 + 0 , 0 , 0 , 0 , 0 , FIFO_u , 0 , 0 , //120 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 //128 +} ; + +static inline void kill_all(pidservice_t *apids) +{ + log_flow() ; + + unsigned int j = napid ; + while (j--) kill(apids[j].pid, SIGKILL) ; +} + +static int pidservice_get_id(pidservice_t *apids, unsigned int id) +{ + log_flow() ; + + unsigned int pos = 0 ; + + for (; pos < napid ; pos++) { + if (apids[pos].vertex == id) + return (unsigned int) pos ; + } + return -1 ; +} + +static int check_action(pidservice_t *apids, unsigned int pos, unsigned int receive, unsigned int what) +{ + unsigned int p = char2enum[receive] ; + unsigned char action = actions[what][p] ; + + switch(action) { + + case SERVICE_ACTION_GOTIT: + FLAGS_SET(apids[pos].state, (!what ? SVC_FLAGS_UP : SVC_FLAGS_DOWN)) ; + return 1 ; + + case SERVICE_ACTION_FATAL: + FLAGS_SET(apids[pos].state, SVC_FLAGS_FATAL) ; + return -1 ; + + case SERVICE_ACTION_WAIT: + return 0 ; + + case SERVICE_ACTION_UNKNOWN: + default: + log_die(LOG_EXIT_ZERO,"invalid action -- please make a bug report") ; + } + +} + +static void notify(pidservice_t *apids, unsigned int pos, char const *sig, unsigned int what) +{ + log_flow() ; + + unsigned int i = 0, idx = 0 ; + char fmt[UINT_FMT] ; + uint8_t flag = what ? SVC_FLAGS_DOWN : SVC_FLAGS_UP ; + + for (; i < apids[pos].nnotif ; i++) { + + for (idx = 0 ; idx < napid ; idx++) { + + if (apids[pos].notif[i] == apids[idx].vertex && !FLAGS_ISSET(apids[idx].state, flag)) { + + size_t nlen = uint_fmt(fmt, apids[pos].aresid) ; + fmt[nlen] = 0 ; + size_t len = nlen + 1 + 2 ; + char s[len + 1] ; + auto_strings(s, fmt, ":", sig, "@") ; + + log_trace("sends notification ", sig, " to: ", pares[apids[idx].aresid].sa.s + pares[apids[idx].aresid].name, " from: ", pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].name) ; + + if (write(apids[idx].pipe[1], s, strlen(s)) < 0) + log_dieusys(LOG_EXIT_SYS, "send notif to: ", pares[apids[idx].aresid].sa.s + pares[apids[idx].aresid].name) ; + } + } + } +} + +/** + * @what: up or down + * @success: 0 fail, 1 win + * */ +static void announce(unsigned int pos, pidservice_t *apids, unsigned int what, unsigned int success, unsigned int exitcode) +{ + log_flow() ; + + int fd = 0 ; + char fmt[UINT_FMT] ; + char const *name = pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].name ; + char const *scandir = pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].live.scandir ; + size_t scandirlen = strlen(scandir) ; + char file[scandirlen + 6] ; + + auto_strings(file, scandir, "/down") ; + + uint8_t flag = what ? SVC_FLAGS_DOWN : SVC_FLAGS_UP ; + + if (success) { + + if (pares[apids[pos].aresid].type == TYPE_CLASSIC) { + + fd = open_trunc(file) ; + if (fd < 0) + log_dieusys(LOG_EXIT_SYS, "create file: ", scandir) ; + fd_close(fd) ; + } + + notify(apids, pos, "F", what) ; + + fmt[uint_fmt(fmt, exitcode)] = 0 ; + + log_1_warn("Unable to ", reloadmsg == 1 ? "restart" : reloadmsg > 1 ? "reload" : what ? "stop" : "start", " service: ", name, " -- exited with signal: ", fmt) ; + + FLAGS_SET(apids[pos].state, SVC_FLAGS_BLOCK|SVC_FLAGS_FATAL) ; + + } else { + + if (!state_messenger(&pares[apids[pos].aresid], STATE_FLAGS_ISUP, what ? STATE_FLAGS_FALSE : STATE_FLAGS_TRUE)) + log_dieusys(LOG_EXIT_SYS, "send message to state of: ", name) ; + + if (!pares[apids[pos].aresid].execute.down && pares[apids[pos].aresid].type == TYPE_CLASSIC) { + + if (!what) { + + if (!access(scandir, F_OK)) { + log_trace("delete down file: ", file) ; + if (unlink(file) < 0 && errno != ENOENT) + log_warnusys("delete down file: ", file) ; + } + + } else { + + fd = open_trunc(file) ; + if (fd < 0) + log_dieusys(LOG_EXIT_SYS, "create file: ", file) ; + fd_close(fd) ; + } + } + + notify(apids, pos, what ? "D" : "U", what) ; + + FLAGS_CLEAR(apids[pos].state, SVC_FLAGS_BLOCK) ; + FLAGS_SET(apids[pos].state, flag|SVC_FLAGS_UNBLOCK) ; + + log_info("Successfully ", reloadmsg == 1 ? "restarted" : reloadmsg > 1 ? "reloaded" : what ? "stopped" : "started", " service: ", name) ; + } +} + +static int async_deps(pidservice_t *apids, unsigned int i, unsigned int what, ssexec_t *info, graph_t *graph, tain *deadline) +{ + log_flow() ; + + int r ; + unsigned int pos = 0, id = 0, ilog = 0, idx = 0 ; + char buf[(UINT_FMT*2)*SS_MAX_SERVICE + 1] ; + + tain dead ; + tain_now_set_stopwatch_g() ; + tain_add_g(&dead, deadline) ; + + iopause_fd x = { .fd = apids[i].pipe[0], .events = IOPAUSE_READ, 0 } ; + + unsigned int n = apids[i].nedge ; + unsigned int visit[n] ; + + graph_array_init_single(visit, n) ; + + while (pos < n) { + + r = iopause_g(&x, 1, &dead) ; + + if (r < 0) + log_dieusys(LOG_EXIT_SYS, "iopause") ; + + if (!r) { + errno = ETIMEDOUT ; + log_dieusys(LOG_EXIT_SYS,"time out", pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name) ; + } + + if (x.revents & IOPAUSE_READ) { + + memset(buf, 0, sizeof(buf)) ; + r = read(apids[i].pipe[0], buf, sizeof(buf)) ; + if (r < 0) + log_dieu(LOG_EXIT_SYS, "read from pipe") ; + buf[r] = 0 ; + + idx = 0 ; + + while (r != -1) { + /** The buf might contain multiple signal coming + * from the dependencies if they finished before + * the start of this read process. Check every + * signal received.*/ + r = get_len_until(buf + idx, '@') ; + + if (r < 0) + /* no more signal */ + goto next ; + + char line[r + 1] ; + memcpy(line, buf + idx, r) ; + line[r] = 0 ; + + idx += r + 1 ; + + /** + * the received string have the format: + * index_of_the_ares_array_of_the_service_dependency:signal_receive + * + * typically: + * - 10:D + * - 30:u + * - ... + * + * Split it and check the signal receive.*/ + int sep = get_len_until(line, ':') ; + if (sep < 0) + log_die(LOG_EXIT_SYS, "received bad signal format -- please make a bug report") ; + + unsigned int c = line[sep + 1] ; + char pc[2] = { c, 0 } ; + line[sep] = 0 ; + + if (!uint0_scan(line, &id)) + log_dieusys(LOG_EXIT_SYS, "retrieve service number -- please make a bug report") ; + + ilog = id ; + + log_trace(pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name, " acknowledges: ", pc, " from: ", pares[ilog].sa.s + pares[ilog].name) ; + + if (!visit[pos]) { + + id = pidservice_get_id(apids, id) ; + if (id < 0) + log_dieu(LOG_EXIT_SYS, "get apidservice id -- please make a bug report") ; + + id = check_action(apids, id, c, what) ; + if (id < 0) + log_die(LOG_EXIT_SYS, "service dependency: ", pares[ilog].sa.s + pares[ilog].name, " of: ", pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name," crashed") ; + + if (!id) + continue ; + + visit[pos++]++ ; + } + } + } + next: + + } + + return 1 ; +} + +static int handle_signal(pidservice_t *apids, unsigned int what, graph_t *graph, ssexec_t *info) +{ + log_flow() ; + + int ok = 0 ; + + for (;;) { + + int s = selfpipe_read() ; + switch (s) { + + case -1 : log_dieusys(LOG_EXIT_SYS,"selfpipe_read") ; + case 0 : return ok ; + case SIGCHLD : + + for (;;) { + + unsigned int pos = 0 ; + int wstat ; + pid_t r = wait_nohang(&wstat) ; + + if (r < 0) { + + if (errno = ECHILD) + break ; + else + log_dieusys(LOG_EXIT_SYS,"wait for children") ; + + } else if (!r) break ; + + for (; pos < napid ; pos++) + if (apids[pos].pid == r) + break ; + + if (pos < napid) { + + if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) { + + announce(pos, apids, what, 0, 0) ; + + } else { + + ok = WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat) ; + announce(pos, apids, what, 1, ok) ; + + kill_all(apids) ; + break ; + } + + npid-- ; + } + } + break ; + case SIGTERM : + case SIGKILL : + case SIGINT : + log_1_warn("received SIGINT, aborting transaction") ; + kill_all(apids) ; + ok = 111 ; + break ; + default : log_die(LOG_EXIT_SYS, "unexpected data in selfpipe") ; + } + } + + return ok ; +} + +/** this following function come from: + * https://git.skarnet.org/cgi-bin/cgit.cgi/s6-rc/tree/src/s6-rc/s6-rc.c#n111 + * under license ISC where parameters was modified */ +static uint32_t compute_timeout (uint32_t timeout, tain *deadline) +{ + uint32_t t = timeout ; + int globalt ; + tain globaltto ; + tain_sub(&globaltto, deadline, &STAMP) ; + globalt = tain_to_millisecs(&globaltto) ; + if (!globalt) globalt = 1 ; + if (globalt > 0 && (!t || (unsigned int)globalt < t)) + t = (uint32_t)globalt ; + return t ; +} + +static int doit(pidservice_t *sv, unsigned int what, tain *deadline) +{ + log_flow() ; + + uint8_t type = pares[sv->aresid].type ; + + pid_t pid ; + int wstat ; + + char tfmt[UINT32_FMT] ; + + unsigned int timeout = 0 ; + if (!what) + timeout = compute_timeout(type == TYPE_ONESHOT ? pares[sv->aresid].execute.timeout.up : pares[sv->aresid].execute.timeout.kill, deadline) ; + else + timeout = compute_timeout(type == TYPE_ONESHOT ? pares[sv->aresid].execute.timeout.down : pares[sv->aresid].execute.timeout.finish, deadline) ; + + tfmt[uint_fmt(tfmt, timeout)] = 0 ; + + if (type == TYPE_CLASSIC) { + + char *scandir = pares[sv->aresid].sa.s + pares[sv->aresid].live.scandir ; + + if (updown[2] == 'U' || updown[2] == 'D' || updown[2] == 'R') { + + if (!pares[sv->aresid].notify) + updown[2] = updown[2] == 'U' ? 'u' : updown[2] == 'D' ? 'd' : updown[2] == 'R' ? 'r' : updown[2] ; + + } + + char const *newargv[8] ; + unsigned int m = 0 ; + + newargv[m++] = "s6-svc" ; + newargv[m++] = data ; + + if (opt_updown) + newargv[m++] = updown ; + + newargv[m++] = "-T" ; + newargv[m++] = tfmt ; + newargv[m++] = "--" ; + newargv[m++] = scandir ; + newargv[m++] = 0 ; + + log_trace("sending ", opt_updown ? newargv[2] : "", opt_updown ? " " : "", data, " to: ", scandir) ; + + pid = child_spawn0(newargv[0], newargv, (char const *const *) environ) ; + + if (waitpid_nointr(pid, &wstat, 0) < 0) + log_warnusys_return(LOG_EXIT_ZERO, "wait for s6-svc") ; + + if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) + return WEXITSTATUS(wstat) ; + else + return WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat) ; + + } else if (type == TYPE_ONESHOT) { + + char *sa = pares[sv->aresid].sa.s ; + char *name = sa + pares[sv->aresid].name ; + size_t namelen = strlen(name) ; + char *home = pares[sv->aresid].sa.s + pares[sv->aresid].path.home ; + size_t homelen = strlen(home) ; + + char script[homelen + SS_SYSTEM_LEN + SS_SERVICE_LEN + SS_SVC_LEN + 1 + namelen + 7 + 1] ; + auto_strings(script, home, SS_SYSTEM, SS_SERVICE, SS_SVC, "/", name) ; + + char *oneshotdir = pares[sv->aresid].sa.s + pares[sv->aresid].live.oneshotddir ; + char *scandir = pares[sv->aresid].sa.s + pares[sv->aresid].live.scandir ; + char oneshot[strlen(oneshotdir) + 2 + 1] ; + auto_strings(oneshot, oneshotdir, "/s") ; + + char const *newargv[11] ; + unsigned int m = 0 ; + newargv[m++] = "s6-sudo" ; + newargv[m++] = VERBOSITY >= 4 ? "-vel0" : "-el0" ; + newargv[m++] = "-t" ; + newargv[m++] = "30000" ; + newargv[m++] = "-T" ; + newargv[m++] = tfmt ; + newargv[m++] = "--" ; + newargv[m++] = oneshot ; + newargv[m++] = !what ? "up" : "down" ; + newargv[m++] = script ; + newargv[m++] = 0 ; + + log_trace("sending ", !what ? "up" : "down", " to: ", scandir) ; + + pid = child_spawn0(newargv[0], newargv, (char const *const *) environ) ; + + if (waitpid_nointr(pid, &wstat, 0) < 0) + log_warnusys_return(LOG_EXIT_ZERO, "wait for s6-sudo") ; + + if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) + return WEXITSTATUS(wstat) ; + else + return WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat) ; + + } else if (type == TYPE_BUNDLE || type == TYPE_MODULE) { + + return svc_compute_ns(&pares[sv->aresid], what, timeout, PINFO, updown, opt_updown, reloadmsg, data, PROPAGATE) ; ; + } + // never happen, let compiler to be happy + return 0 ; +} + +static int async(pidservice_t *apids, unsigned int i, unsigned int what, ssexec_t *info, graph_t *graph, tain *deadline) +{ + log_flow() ; + + int e = 0 ; + + char *name = graph->data.s + genalloc_s(graph_hash_t,&graph->hash)[apids[i].vertex].vertex ; + + log_trace("beginning of the process of: ", name) ; + + if (FLAGS_ISSET(apids[i].state, (!what ? SVC_FLAGS_DOWN : SVC_FLAGS_UP))) { + + if (!FLAGS_ISSET(apids[i].state, SVC_FLAGS_BLOCK)) { + + FLAGS_SET(apids[i].state, SVC_FLAGS_BLOCK) ; + + if (apids[i].nedge) + if (!async_deps(apids, i, what, info, graph, deadline)) + log_warnu_return(LOG_EXIT_ZERO, !what ? "start" : "stop", " dependencies of service: ", name) ; + + e = doit(&apids[i], what, deadline) ; + + } else { + + log_trace("skipping service: ", name, " -- already in ", what ? "stopping" : "starting", " process") ; + + notify(apids, i, what ? "d" : "u", what) ; + + } + + } else { + + /** do not notify here, the handle will make it for us */ + log_trace("skipping service: ", name, " -- already ", what ? "down" : "up") ; + + } + + return e ; +} + +int svc_launch(pidservice_t *apids, unsigned int len, uint8_t what, graph_t *graph, resolve_service_t *ares, uint8_t timeout, ssexec_t *info, char const *rise, uint8_t rise_opt, uint8_t msg, char const *signal, uint8_t propagate) +{ + log_flow() ; + + unsigned int e = 0, pos = 0 ; + int r ; + pid_t pid ; + pidservice_t apidservicetable[len] ; + pidservice_t_ref apidservice = apidservicetable ; + tain deadline ; + + npid = 0 ; + PINFO = info ; + PROPAGATE = propagate ; + napid = len ; + auto_strings(updown, rise) ; + opt_updown = rise_opt ; + reloadmsg = msg ; + auto_strings(data, signal) ; + pares = ares ; + + if (timeout) + tain_from_millisecs(&deadline, timeout) ; + else + deadline = tain_infinite_relative ; + + tain_now_set_stopwatch_g() ; + tain_add_g(&deadline, &deadline) ; + + int spfd = selfpipe_init() ; + + if (spfd < 0) + log_dieusys(LOG_EXIT_SYS, "selfpipe_init") ; + + if (!selfpipe_trap(SIGCHLD) || + !selfpipe_trap(SIGINT) || + !selfpipe_trap(SIGKILL) || + !selfpipe_trap(SIGTERM) || + !sig_altignore(SIGPIPE)) + log_dieusys(LOG_EXIT_SYS, "selfpipe_trap") ; + + iopause_fd x = { .fd = spfd, .events = IOPAUSE_READ, .revents = 0 } ; + + for (; pos < napid ; pos++) { + + apidservice[pos] = apids[pos] ; + + if (pipe(apidservice[pos].pipe) < 0) + log_dieusys(LOG_EXIT_SYS, "pipe"); + + } + + for (pos = 0 ; pos < napid ; pos++) { + + pid = fork() ; + + if (pid < 0) + log_dieusys(LOG_EXIT_SYS, "fork") ; + + if (!pid) { + + selfpipe_finish() ; + + close(apidservice[pos].pipe[1]) ; + + e = async(apidservice, pos, what, info, graph, &deadline) ; + + goto end ; + } + + apidservice[pos].pid = pid ; + + close(apidservice[pos].pipe[0]) ; + + npid++ ; + } + + while (npid) { + + r = iopause_g(&x, 1, &deadline) ; + + if (r < 0) + log_dieusys(LOG_EXIT_SYS, "iopause") ; + + if (!r) { + errno = ETIMEDOUT ; + log_diesys(LOG_EXIT_SYS,"time out") ; + } + + if (x.revents & IOPAUSE_READ) { + e = handle_signal(apidservice, what, graph, info) ; + + if (e) + break ; + } + } + + selfpipe_finish() ; + end: + for (pos = 0 ; pos < napid ; pos++) { + close(apidservice[pos].pipe[1]) ; + close(apidservice[pos].pipe[0]) ; + } + + return e ; +}