diff --git a/src/lib66/exec/ssexec_tree_wrapper.c b/src/lib66/exec/ssexec_tree_wrapper.c new file mode 100644 index 0000000000000000000000000000000000000000..e0f80ec56cd24aed0821466f899964d15792639e --- /dev/null +++ b/src/lib66/exec/ssexec_tree_wrapper.c @@ -0,0 +1,219 @@ +/* + * ssexec_reload.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 <oblibs/log.h> + +#include <skalibs/sgetopt.h> + +#include <66/ssexec.h> +#include <66/config.h> + +int ssexec_tree_wrapper(int argc, char const *const *argv, ssexec_t *info) +{ + log_flow() ; + + if (!argv[1]) { + PROG = "tree" ; + log_usage(usage_tree) ; + } + + int r, n = 0, i = 0 ; + uint8_t ctl = 0 ; + ssexec_func_t_ref func = 0 ; + char const *nargv[argc + 1] ; + + if (!strcmp(argv[1], "create")) { + + nargv[n++] = PROG ; + + info->prog = PROG ; + info->help = help_tree ; + info->usage = usage_tree ; + func = &ssexec_tree ; + + argc-- ; + argv++ ; + + } else if (!strcmp(argv[1], "admin")) { + + nargv[n++] = PROG ; + + info->prog = PROG ; + info->help = help_tree ; + info->usage = usage_tree ; + func = &ssexec_tree ; + + argc-- ; + argv++ ; + + } else if (!strcmp(argv[1], "remove")) { + + nargv[n++] = PROG ; + nargv[n++] = "-R" ; + + info->prog = PROG ; + info->help = help_tree ; + info->usage = usage_tree ; + func = &ssexec_tree ; + + argc-- ; + argv++ ; + + } else if (!strcmp(argv[1], "enable")) { + + nargv[n++] = PROG ; + nargv[n++] = "-E" ; + + info->prog = PROG ; + info->help = help_tree ; + info->usage = usage_tree ; + func = &ssexec_tree ; + + argc-- ; + argv++ ; + + } else if (!strcmp(argv[1], "disable")) { + + nargv[n++] = PROG ; + nargv[n++] = "-D" ; + + info->prog = PROG ; + info->help = help_tree ; + info->usage = usage_tree ; + + func = &ssexec_tree ; + + argc-- ; + argv++ ; + + } else if (!strcmp(argv[1], "current")) { + + nargv[n++] = PROG ; + nargv[n++] = "-c" ; + + info->prog = PROG ; + info->help = help_tree ; + info->usage = usage_tree ; + + func = &ssexec_tree ; + + argc-- ; + argv++ ; + + } else if (!strcmp(argv[1], "up")) { + + info->prog = PROG ; + info->help = help_treectl ; + info->usage = usage_treectl ; + func = &ssexec_treectl ; + ctl++ ; + + } else if (!strcmp(argv[1], "down")) { + + info->prog = PROG ; + info->help = help_treectl ; + info->usage = usage_treectl ; + func = &ssexec_treectl ; + ctl++ ; + + } else if (!strcmp(argv[1], "unsupervise")) { + + info->prog = PROG ; + info->help = help_treectl ; + info->usage = usage_treectl ; + func = &ssexec_treectl ; + ctl++ ; + + } else { + + log_usage(usage_tree) ; + } + + { + subgetopt l = SUBGETOPT_ZERO ; + + int f = 0 ; + for (;;) { + + int opt = subgetopt_r(argc, argv, "", &l) ; + if (opt == -1) break ; + + for (i = 0 ; i < n ; i++) { + + if (!argv[l.ind]) + log_usage(info->usage) ; + + if (l.arg) { + + if (!strcmp(nargv[i],argv[l.ind - 2]) || !strcmp(nargv[i],l.arg)) + f = 1 ; + + } else { + + if (!strcmp(nargv[i],argv[l.ind])) + f = 1 ; + } + } + + if (!f) { + + if (l.arg) { + // distinction between e.g -enano and -e nano + if (argv[l.ind - 1][0] != '-') + nargv[n++] = argv[l.ind - 2] ; + + nargv[n++] = argv[l.ind - 1] ; + + } else { + + nargv[n++] = argv[l.ind] ; + } + } + f = 0 ; + } + argc -= l.ind ; argv += l.ind ; + } + + for (i = 0 ; i < argc ; i++ , argv++) + nargv[n++] = *argv ; + + nargv[n] = 0 ; + + if (ctl) { + /* swap the command and options e.g. + * down -f <treename> <-> -f down <treename> */ + if (n > 2) { + /* swap the command and options e.g. + * down -f <-> -f down */ + nargv[n] = nargv[n-1] ; + nargv[n-1] = nargv[0] ; + nargv[++n] = 0 ; + + } else if (nargv[n-1][0] == '-') { + nargv[n++] = nargv[0] ; + nargv[n] = 0 ; + } else { + nargv[n] = nargv[n-1] ; + nargv[n-1] = nargv[0] ; + nargv[++n] = 0 ; + } + + } + + r = (*func)(n, nargv, info) ; + + return r ; +} diff --git a/src/lib66/exec/ssexec_treectl.c b/src/lib66/exec/ssexec_treectl.c new file mode 100644 index 0000000000000000000000000000000000000000..12ca53fd124ee3c410c828c5c6a402790100ba48 --- /dev/null +++ b/src/lib66/exec/ssexec_treectl.c @@ -0,0 +1,1015 @@ +/* + * ssexec_treectl.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 <sys/stat.h>//S_IFREG,umask +#include <sys/types.h>//pid_t +#include <fcntl.h>//O_RDWR +#include <unistd.h>//dup2,setsid,chdir,fork +#include <sys/ioctl.h> +#include <stdint.h>//uint8_t +#include <stdlib.h>//realpath +#include <string.h>//strdup + +#include <oblibs/string.h> +#include <oblibs/types.h> +#include <oblibs/log.h> +#include <oblibs/sastr.h> +#include <oblibs/obgetopt.h> +#include <oblibs/files.h> +#include <oblibs/graph.h> +#include <oblibs/directory.h> + +#include <skalibs/types.h> +#include <skalibs/stralloc.h> +#include <skalibs/djbunix.h> +#include <skalibs/posixplz.h> +#include <skalibs/tai.h> +#include <skalibs/selfpipe.h> +#include <skalibs/sig.h> +#include <skalibs/iopause.h> + +#include <66/ssexec.h> +#include <66/constants.h> +#include <66/tree.h> +#include <66/svc.h>//scandir_ok +#include <66/utils.h> +#include <66/graph.h> + +#include <s6/ftrigr.h> +#include <s6/ftrigw.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 up/down +#define FLAGS_FATAL (1 << 6) // 64 process crashed + +static unsigned int napid = 0 ; +static unsigned int npid = 0 ; + +static resolve_tree_t_ref pares = 0 ; +static unsigned int *pareslen = 0 ; +static uint8_t reloadmsg = 0 ; + +typedef struct pidtree_s pidtree_t, *pidtree_t_ref ; +struct pidtree_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 tree is started/stopped */ + unsigned int notif[SS_MAX_SERVICE + 1] ; +} ; +#define PIDTREE_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 tree_action_e tree_action_t, *tree_action_t_ref ; +enum tree_action_e +{ + TREE_ACTION_GOTIT = 0, + TREE_ACTION_WAIT, + TREE_ACTION_FATAL, + TREE_ACTION_UNKNOWN +} ; + +static const unsigned char actions[2][7] = { + // u U d D F b B + { TREE_ACTION_WAIT, TREE_ACTION_GOTIT, TREE_ACTION_UNKNOWN, TREE_ACTION_UNKNOWN, TREE_ACTION_FATAL, TREE_ACTION_WAIT, TREE_ACTION_WAIT }, // !what -> up + { TREE_ACTION_UNKNOWN, TREE_ACTION_UNKNOWN, TREE_ACTION_WAIT, TREE_ACTION_GOTIT, TREE_ACTION_FATAL, TREE_ACTION_WAIT, TREE_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 unsigned int lookup (char const *const *table, char const *signal) +{ + log_flow() ; + + unsigned int i = 0 ; + for (; table[i] ; i++) if (!strcmp(signal, table[i])) break ; + return i ; +} + +static inline unsigned int parse_signal (char const *signal) +{ + log_flow() ; + + static char const *const signal_table[] = { + "up", + "down", + "unsupervise", + 0 + } ; + unsigned int i = lookup(signal_table, signal) ; + if (!signal_table[i]) log_usage(usage_treectl) ; + return i ; +} + +static void all_redir_fd(void) +{ + log_flow() ; + + int fd ; + while((fd = open("/dev/tty",O_RDWR|O_NOCTTY)) >= 0) { + + if (fd >= 3) + break ; + } + + dup2 (fd,0) ; + dup2 (fd,1) ; + dup2 (fd,2) ; + fd_close(fd) ; + + if (setsid() < 0) + log_dieusys(LOG_EXIT_SYS,"setsid") ; + + if ((chdir("/")) < 0) + log_dieusys(LOG_EXIT_SYS,"chdir") ; + + ioctl(0,TIOCSCTTY,1) ; + + umask(022) ; +} + +void tree_resolve_array_free(resolve_tree_t *ares, unsigned int areslen) +{ + + unsigned int pos = 0 ; + for (; pos < areslen ; pos++) + stralloc_free(&ares[pos].sa) ; +} + +static inline void kill_all(pidtree_t *apidt) +{ + log_flow() ; + + unsigned int j = napid ; + while (j--) kill(apidt[j].pid, SIGKILL) ; +} + +static pidtree_t pidtree_init(unsigned int len) +{ + log_flow() ; + + pidtree_t pids = PIDTREE_ZERO ; + + if (len > SS_MAX_SERVICE) + log_die(LOG_EXIT_SYS, "too many trees") ; + + graph_array_init_single(pids.edge, len) ; + + return pids ; +} + + +static int pidtree_get_id(pidtree_t *apidt, unsigned int id) +{ + log_flow() ; + + unsigned int pos = 0 ; + + for (; pos < napid ; pos++) { + if (apidt[pos].vertex == id) + return (unsigned int) pos ; + } + return -1 ; +} + + +static void notify(pidtree_t *apidt, 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 < apidt[pos].nnotif ; i++) { + + for (idx = 0 ; idx < napid ; idx++) { + + if (apidt[pos].notif[i] == apidt[idx].vertex && !FLAGS_ISSET(apidt[idx].state, flag)) { + + size_t nlen = uint_fmt(fmt, apidt[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[apidt[idx].aresid].sa.s + pares[apidt[idx].aresid].name, " from: ", pares[apidt[pos].aresid].sa.s + pares[apidt[pos].aresid].name) ; + + if (write(apidt[idx].pipe[1], s, strlen(s)) < 0) + log_dieusys(LOG_EXIT_SYS, "send notif to: ", pares[apidt[idx].aresid].sa.s + pares[apidt[idx].aresid].name) ; + } + } + } +} + +/** + * @what: up or down + * @success: 0 fail, 1 win + * */ +static void announce(unsigned int pos, pidtree_t *apidt, char const *base, unsigned int what, unsigned int success, unsigned int exitcode) +{ + log_flow() ; + + char fmt[UINT_FMT] ; + char const *treename = pares[apidt[pos].aresid].sa.s + pares[apidt[pos].aresid].name ; + + resolve_tree_t tres = RESOLVE_TREE_ZERO ; + resolve_wrapper_t_ref wres = resolve_set_struct(DATA_TREE, &tres) ; + + uint8_t flag = what ? FLAGS_DOWN : FLAGS_UP ; + + if (!what || what == 2) + if (!resolve_modify_field_g(wres, base, treename, E_RESOLVE_TREE_INIT, what ? (success ? "1" : "0") : (success ? "0" : "1"))) + log_dieusys(LOG_EXIT_SYS, "modify resolve file of: ", treename) ; + + if (success) { + + notify(apidt, pos, "F", what) ; + + fmt[uint_fmt(fmt, exitcode)] = 0 ; + + log_1_warnu(reloadmsg == 0 ? "start" : reloadmsg > 1 ? "unsupervise" : what == 0 ? "start" : "stop", " tree: ", treename, " -- exited with signal: ", fmt) ; + + FLAGS_SET(apidt[pos].state, FLAGS_BLOCK|FLAGS_FATAL) ; + + } else { + + notify(apidt, pos, what ? "D" : "U", what) ; + + FLAGS_CLEAR(apidt[pos].state, FLAGS_BLOCK) ; + FLAGS_SET(apidt[pos].state, flag|FLAGS_UNBLOCK) ; + + log_info("Successfully ", reloadmsg == 0 ? "started" : reloadmsg > 1 ? "unsupervised" : what == 0 ? "started" : "stopped", " tree: ", treename) ; + } + + resolve_free(wres) ; +} + +static void pidtree_init_array(unsigned int *list, unsigned int listlen, pidtree_t *apidt, graph_t *g, resolve_tree_t *ares, unsigned int areslen, ssexec_t *info, uint8_t requiredby) +{ + log_flow() ; + + int r = 0 ; + unsigned int pos = 0 ; + + for (; pos < listlen ; pos++) { + + pidtree_t pids = pidtree_init(g->mlen) ; + + char *name = g->data.s + genalloc_s(graph_hash_t,&g->hash)[list[pos]].vertex ; + + pids.aresid = tree_resolve_array_search(ares, areslen, name) ; + + if (pids.aresid < 0) + log_dieu(LOG_EXIT_SYS,"find ares id of: ", name, " -- please make a bug report") ; + + 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 tree: ", 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 tree: ", 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") ; + + r = tree_isinitialized(info->base.s, name) ; + + if (r < 0) + log_dieu(LOG_EXIT_SYS, "read resolve file of tree: ", name) ; + + if (r) + FLAGS_SET(pids.state, FLAGS_UP) ; + else + FLAGS_SET(pids.state, FLAGS_DOWN) ; + + apidt[pos] = pids ; + } +} + +static int handle_signal(pidtree_t *apidt, 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 (apidt[pos].pid == r) + break ; + + if (pos < napid) { + + if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) { + + announce(pos, apidt, info->base.s, what, 0, 0) ; + + } else { + + ok = WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat) ; + announce(pos, apidt, info->base.s, what, 1, ok) ; + + kill_all(apidt) ; + break ; + } + + npid-- ; + } + } + break ; + case SIGTERM : + case SIGKILL : + case SIGINT : + log_1_warn("received SIGINT, aborting transaction") ; + kill_all(apidt) ; + 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 ssexec_callback(stralloc *sa, ssexec_t *info, unsigned int what) +{ + size_t pos = 0, len = sastr_len(sa), e = 1 ; + + int n = what == 2 ? 2 : 1 ; + int nargc = n + len ; + char const *prog = PROG ; + char const *newargv[nargc] ; + unsigned int m = 0 ; + + newargv[m++] = "treectl" ; + if (what == 2) + newargv[m++] = "-u" ; + + FOREACH_SASTR(sa, pos) + newargv[m++] = sa->s + pos ; + + newargv[m] = 0 ; + + if (!what) { + + PROG = "start" ; + e = ssexec_start(nargc, newargv, info) ; + PROG = prog ; + + } else { + + PROG = "stop" ; + e = ssexec_stop(nargc, newargv, info) ; + PROG = prog ; + } + + return e ; +} + +static int doit(char const *treename, ssexec_t *sinfo, unsigned int what, tain *deadline) +{ + log_flow() ; + + int r, e = 1 ; + ssexec_t info = SSEXEC_ZERO ; + + ssexec_copy(&info, sinfo) ; + + { + info.treename.len = 0 ; + + if (!auto_stra(&info.treename, treename)) + log_die_nomem("stralloc") ; + + info.tree.len = 0 ; + + r = tree_sethome(&info) ; + if (r <= 0) + log_warnu_return(LOG_EXIT_ZERO, "find tree: ", info.treename.s) ; + + if (!tree_get_permissions(info.tree.s, info.owner)) + log_warn_return(LOG_EXIT_ZERO, "You're not allowed to use the tree: ", info.tree.s) ; + } + + if (!tree_isinitialized(info.base.s, info.treename.s) && !what) { + + if (!what) { + + int nargc = 3 ; + char const *prog = PROG ; + char const *newargv[nargc] ; + unsigned int m = 0 ; + + newargv[m++] = "all (child)" ; + newargv[m++] = info.treename.s ; + newargv[m++] = 0 ; + + PROG = "init" ; + if (ssexec_init(nargc, newargv, &info)) + log_warnu_return(LOG_EXIT_ZERO, "initiate services of tree: ", info.treename.s) ; + PROG = prog ; + + } else { + + log_warn ("uninitialized tree: ", info.treename.s) ; + goto end ; + } + } + + { + stralloc sa = STRALLOC_ZERO ; + char const *exclude[2] = { SS_MASTER + 1 , 0 } ; + size_t treelen = info.tree.len + SS_SVDIRS_LEN + SS_RESOLVE_LEN ; + char tree[treelen + 1] ; + + auto_strings(tree, info.tree.s, SS_SVDIRS, SS_RESOLVE) ; + + if (!sastr_dir_get(&sa, tree, exclude, S_IFREG)) + log_dieu(LOG_EXIT_SYS, "get services list from tree: ", info.treename.s) ; + + if (!sa.len) { + + log_info("Empty tree: ", info.treename.s, " -- nothing to do") ; + + } else { + + int r = ssexec_callback(&sa, &info, what) ; + if (r) + goto err ; + + if (what == 2) + log_info("Unsupervised successfully tree: ", info.treename.s) ; + } + + stralloc_free(&sa) ; + } + end: + e = 0 ; + err: + ssexec_free(&info) ; + return e ; +} + +static int check_action(pidtree_t *apidt, unsigned int pos, unsigned int receive, unsigned int what) +{ + unsigned int p = char2enum[receive] ; + unsigned char action = actions[what][p] ; + + switch(action) { + + case TREE_ACTION_GOTIT: + FLAGS_SET(apidt[pos].state, (!what ? FLAGS_UP : FLAGS_DOWN)) ; + return 1 ; + + case TREE_ACTION_FATAL: + FLAGS_SET(apidt[pos].state, FLAGS_FATAL) ; + return -1 ; + + case TREE_ACTION_WAIT: + return 0 ; + + case TREE_ACTION_UNKNOWN: + default: + log_die(LOG_EXIT_ZERO,"invalid action -- please make a bug report") ; + } + +} + +static int async_deps(pidtree_t *apidt, 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 = apidt[i].pipe[0], .events = IOPAUSE_READ, 0 } ; + + unsigned int n = apidt[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[apidt[i].aresid].sa.s + pares[apidt[i].aresid].name) ; + } + + if (x.revents & IOPAUSE_READ) { + + memset(buf, 0, sizeof(buf)) ; + r = read(apidt[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_tree_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[apidt[i].aresid].sa.s + pares[apidt[i].aresid].name, " acknowledges: ", pc, " from: ", pares[ilog].sa.s + pares[ilog].name) ; + + if (!visit[pos]) { + + id = pidtree_get_id(apidt, id) ; + if (id < 0) + log_dieu(LOG_EXIT_SYS, "get apidtree id -- please make a bug report") ; + + id = check_action(apidt, id, c, what) ; + if (id < 0) + log_die(LOG_EXIT_SYS, "tree dependency: ", pares[ilog].sa.s + pares[ilog].name, " of: ", pares[apidt[i].aresid].sa.s + pares[apidt[i].aresid].name," crashed") ; + + if (!id) + continue ; + + visit[pos++]++ ; + } + } + } + next: + + } + + return 1 ; +} + +static int async(pidtree_t *apidt, 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)[apidt[i].vertex].vertex ; + + log_trace("beginning of the process of: ", name) ; + + if (FLAGS_ISSET(apidt[i].state, (!what ? FLAGS_DOWN : FLAGS_UP)) || + /** force to pass through unsupersive process even + * if the tree is marked down */ + FLAGS_ISSET(apidt[i].state, (what ? FLAGS_DOWN : FLAGS_UP)) && what == 2) { + + if (!FLAGS_ISSET(apidt[i].state, FLAGS_BLOCK)) { + + FLAGS_SET(apidt[i].state, FLAGS_BLOCK) ; + + if (apidt[i].nedge) + if (!async_deps(apidt, i, what, info, graph, deadline)) + log_warnu_return(LOG_EXIT_ZERO, !what ? "start" : "stop", " dependencies of tree: ", name) ; + + e = doit(name, info, what, deadline) ; + + } else { + + log_trace("skipping tree: ", name, " -- already in ", what ? "stopping" : "starting", " process") ; + + notify(apidt, 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(pidtree_t *apidt, unsigned int what, graph_t *graph, tain *deadline, ssexec_t *info) +{ + log_flow() ; + + unsigned int e = 0, pos = 0 ; + int r ; + pid_t pid ; + pidtree_t apidtreetable[napid] ; + pidtree_t_ref apidtree = apidtreetable ; + + 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++) { + + apidtree[pos] = apidt[pos] ; + + if (pipe(apidtree[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(apidtree[pos].pipe[1]) ; + + e = async(apidtree, pos, what, info, graph, deadline) ; + + goto end ; + } + + apidtree[pos].pid = pid ; + + close(apidtree[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(apidtree, what, graph, info) ; + + if (e) + break ; + } + } + + + selfpipe_finish() ; + end: + for (pos = 0 ; pos < napid ; pos++) { + close(apidtree[pos].pipe[1]) ; + close(apidtree[pos].pipe[0]) ; + } + + return e ; +} + +int ssexec_treectl(int argc, char const *const *argv, ssexec_t *info) +{ + log_flow() ; + + int r, shut = 0, fd ; + tain deadline ; + uint8_t what = 0, requiredby = 0, found = 0 ; + stralloc sa = STRALLOC_ZERO ; + size_t pos = 0 ; + + unsigned int areslen = 0, list[SS_MAX_SERVICE], visit[SS_MAX_SERVICE] ; + resolve_tree_t ares[SS_MAX_SERVICE] ; + resolve_wrapper_t_ref wres = 0 ; + + graph_t graph = GRAPH_ZERO ; + + { + subgetopt l = SUBGETOPT_ZERO ; + + for (;;) + { + int opt = subgetopt_r(argc, argv, OPTS_TREECTL, &l) ; + if (opt == -1) break ; + + switch (opt) { + case 'f' : shut = 1 ; break ; + default : log_usage(usage_treectl) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + if (argc < 1) + log_usage(usage_treectl) ; + + info->treename.len = 0 ; + + if (argv[1]) { + if (!auto_stra(&info->treename, argv[1])) + log_die_nomem("stralloc") ; + } + + if (info->timeout) + tain_from_millisecs(&deadline, info->timeout) ; + else + deadline = tain_infinite_relative ; + + what = parse_signal(*argv) ; + + reloadmsg = what ; + + if (what) + requiredby = 1 ; + + if ((svc_scandir_ok(info->scandir.s)) <= 0) + log_die(LOG_EXIT_SYS,"scandir: ", info->scandir.s," is not running") ; + + graph_build_tree(&graph, info->base.s, E_RESOLVE_TREE_MASTER_ENABLED) ; + + if (!graph.mlen) + log_die(LOG_EXIT_USER, "trees selection is not created -- creates at least one tree") ; + + graph_array_init_single(visit, SS_MAX_SERVICE) ; + + if (!graph_matrix_sort_tosa(&sa, &graph)) + log_dieu(LOG_EXIT_SYS, "get list of trees for graph -- please make a bug report") ; + + FOREACH_SASTR(&sa, pos) { + + char *treename = sa.s + pos ; + + if (tree_resolve_array_search(ares, areslen, treename) < 0) { + + resolve_tree_t tres = RESOLVE_TREE_ZERO ; + /** need to make a copy of the resolve due of the freed + * of the wres struct at the end of the process */ + resolve_tree_t cp = RESOLVE_TREE_ZERO ; + wres = resolve_set_struct(DATA_TREE, &tres) ; + + if (!resolve_read_g(wres, info->base.s, treename)) + log_dieu(LOG_EXIT_SYS, "read resolve file of: ", treename, " -- please make a bug report") ; + + tree_resolve_copy(&cp, &tres) ; + + ares[areslen++] = cp ; + + resolve_free(wres) ; + } + + /** only one tree */ + if (info->treename.len) { + + if (!strcmp(info->treename.s, treename)) + found = 1 ; + else continue ; + } + + r = tree_isinitialized(info->base.s, treename) ; + if (r && !what) { + + log_warn("tree: ", treename," is already up") ; + continue ; + + } else if (!r && what) { + + log_warn("tree: ", treename," is already down") ; + continue ; + + } + + unsigned int l[graph.mlen], c = 0, pos = 0, idx = 0 ; + + idx = graph_hash_vertex_get_id(&graph, treename) ; + + if (!visit[idx]) { + /** avoid double entry */ + list[napid++] = idx ; + visit[idx] = 1 ; + + } + + /** find dependencies of the tree from the graph, do it recursively */ + c = graph_matrix_get_edge_g_sorted_list(l, &graph, treename, 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 (found) + break ; + } + + pidtree_t apidt[graph.mlen] ; + + pares = ares ; + pareslen = &areslen ; + + if (!napid) { + log_warn("no trees -- something wrong, tree ", SS_DEFAULT_TREENAME, " should exist!") ; + r = 0 ; + goto end ; + } + + pidtree_init_array(list, napid, apidt, &graph, ares, areslen, info, requiredby) ; + + if (shut) { + + pid_t pid ; + int wstat = 0 ; + + pid = fork() ; + + if (pid < 0) + log_dieusys(LOG_EXIT_SYS,"fork") ; + + if (!pid) { + + all_redir_fd() ; + + } else { + + if (waitpid_nointr(pid,&wstat, 0) < 0) + log_dieusys(LOG_EXIT_SYS,"wait for child") ; + + if (wstat) + log_die(LOG_EXIT_SYS,"child fail") ; + + r = 0 ; + + goto end ; + } + } + + r = waitit(apidt, what, &graph, &deadline, info) ; + + end: + + if (shut) { + + while((fd = open("/dev/tty",O_RDWR|O_NOCTTY)) >= 0) + if (fd >= 3) + break ; + + dup2 (fd,0) ; + dup2 (fd,1) ; + dup2 (fd,2) ; + fd_close(fd) ; + } + + graph_free_all(&graph) ; + stralloc_free(&sa) ; + tree_resolve_array_free(ares, areslen) ; + + return r ; +}