/* * ssexec_scanctl.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 <fcntl.h> #include <oblibs/log.h> #include <oblibs/string.h> #include <oblibs/environ.h> #include <skalibs/sgetopt.h> #include <skalibs/stralloc.h> #include <skalibs/types.h> #include <skalibs/djbunix.h> #include <skalibs/exec.h> #include <skalibs/env.h> #include <skalibs/bytestr.h> #include <s6/config.h> #include <66/svc.h> #include <66/utils.h> #include <66/ssexec.h> static char TMPENV[MAXENV + 1] ; 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[] = { "start", "stop", "reload", "quit", "nuke", "zombies", 0 } ; unsigned int i = lookup(signal_table, signal) ; if (!signal_table[i]) i = 6 ; return i ; } static int send_signal(char const *scandir, char const *signal) { log_flow() ; unsigned int sig = 0 ; size_t siglen = strlen(signal) ; char csig[siglen + 1] ; sig = parse_signal(signal) ; if (sig < 6) { switch(sig) { /** start signal, should never happens */ case 0: return 1 ; case 1: csig[0] = 't' ; csig[1] = 0 ; break ; case 2: csig[0] = 'h' ; csig[1] = 0 ; break ; case 3: csig[0] = 'q' ; csig[1] = 0 ; break ; case 4: csig[0] = 'n' ; csig[1] = 0 ; break ; case 5: csig[0] = 'z' ; csig[1] = 0 ; break ; default: break ; } } else { auto_strings(csig,signal) ; } return svc_scandir_send(scandir,csig) ; } static void scandir_up(char const *scandir, unsigned int timeout, unsigned int notif, char const *const *envp) { int r ; r = svc_scandir_ok(scandir) ; if (r < 0) log_dieusys(LOG_EXIT_SYS, "check: ", scandir) ; if (r) { log_trace("scandir: ",scandir," already running") ; return ; } unsigned int no = notif ? 2 : 0 ; char const *newup[6 + no] ; unsigned int m = 0 ; char fmt[UINT_FMT] ; fmt[uint_fmt(fmt, timeout)] = 0 ; char snotif[UINT_FMT] ; snotif[uint_fmt(snotif, notif)] = 0 ; newup[m++] = S6_BINPREFIX "s6-svscan" ; if (no) { newup[m++] = "-d" ; newup[m++] = snotif ; } newup[m++] = "-t" ; newup[m++] = fmt ; newup[m++] = "--" ; newup[m++] = scandir ; newup[m++] = 0 ; xexec_ae(newup[0], newup, envp) ; } int ssexec_scanctl(int argc, char const *const *argv, ssexec_t *info) { int r ; uid_t owner = MYUID ; unsigned int timeout = 0, notif = 0, sig = 0 ; char const *newenv[MAXENV+1] ; char const *const *genv = 0 ; char const *const *genvp = (char const *const *)environ ; char const *signal ; stralloc scandir = STRALLOC_ZERO ; stralloc envdir = STRALLOC_ZERO ; { subgetopt l = SUBGETOPT_ZERO ; for (;;) { int opt = subgetopt_r(argc,argv, OPTS_SCANCTL, &l) ; if (opt == -1) break ; switch (opt) { case 'o' : if (MYUID) log_die(LOG_EXIT_USER, "only root can use -o option") ; if (!youruid(&owner,l.arg)) log_dieusys(LOG_EXIT_SYS,"get uid of: ",l.arg) ; break ; case 'd' : if (!uint0_scan(l.arg, ¬if)) log_usage(usage_scanctl) ; if (notif < 3) log_die(LOG_EXIT_USER, "notification fd must be 3 or more") ; if (fcntl(notif, F_GETFD) < 0) log_diesys(LOG_EXIT_USER, "invalid notification fd") ; break ; case 't' : if (!uint0_scan(l.arg, &timeout)) log_usage(usage_scanctl) ; break ; case 'e' : if(!auto_stra(&envdir,l.arg)) log_die_nomem("stralloc") ; break ; default : log_usage(usage_scanctl) ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 1) log_usage(usage_scanctl) ; signal = argv[0] ; r = set_livedir(&scandir) ; if (r < 0) log_die(LOG_EXIT_USER,"live: ",scandir.s," must be an absolute path") ; if (!r) log_dieusys(LOG_EXIT_SYS,"set live directory") ; r = set_livescan(&scandir,owner) ; if (r < 0) log_die(LOG_EXIT_USER,"scandir: ", scandir.s, " must be an absolute path") ; if (!r) log_dieusys(LOG_EXIT_SYS,"set scandir directory") ; if (envdir.len) { stralloc modifs = STRALLOC_ZERO ; if (envdir.s[0] != '/') log_die(LOG_EXIT_USER,"environment: ",envdir.s," must be an absolute path") ; if (!environ_clean_envfile_unexport(&modifs,envdir.s)) log_dieu(LOG_EXIT_SYS,"clean environment file of: ",envdir.s) ; size_t envlen = env_len(genvp) ; size_t n = env_len(genvp) + 1 + byte_count(modifs.s,modifs.len,'\0') ; size_t mlen = modifs.len ; memcpy(TMPENV,modifs.s,mlen) ; TMPENV[mlen] = 0 ; if (!env_merge(newenv, n, genvp, envlen, TMPENV, mlen)) log_dieu(LOG_EXIT_SYS,"merge environment") ; stralloc_free(&modifs) ; genv = newenv ; } else genv = genvp ; sig = parse_signal(signal) ; if (!sig) { char scan[scandir.len + 1] ; auto_strings(scan,scandir.s) ; stralloc_free(&envdir) ; stralloc_free(&scandir) ; scandir_up(scan,timeout,notif,genv) ; /** if already running, scandir_up() return */ return 0 ; } r = svc_scandir_ok(scandir.s) ; if (!r) log_diesys(LOG_EXIT_SYS,"scandir: ",scandir.s," is not running") ; else if (r < 0) log_dieusys(LOG_EXIT_SYS, "check: ", scandir.s) ; if (send_signal(scandir.s,signal) <= 0) goto err ; stralloc_free(&scandir) ; return 0 ; err: stralloc_free(&scandir) ; return 111 ; }