-
Eric Vidal authored
remove svc_send.c file
Eric Vidal authoredremove svc_send.c file
svc_launch.c 20.02 KiB
/*
* 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 ;
}
unsigned int compute_timeout(unsigned int idx, unsigned int what)
{
unsigned int timeout = 0 ;
if (!what) {
if (pares[idx].type == TYPE_ONESHOT && pares[idx].execute.timeout.up)
timeout = pares[idx].execute.timeout.up ;
else if (pares[idx].type == TYPE_CLASSIC && pares[idx].execute.timeout.kill)
timeout = pares[idx].execute.timeout.kill ;
} else {
if (pares[idx].type == TYPE_ONESHOT && pares[idx].execute.timeout.down)
timeout = pares[idx].execute.timeout.down ;
else if (pares[idx].type == TYPE_CLASSIC && pares[idx].execute.timeout.finish)
timeout = pares[idx].execute.timeout.finish ;
}
if (!timeout && PINFO->opt_timeout)
timeout = PINFO->timeout ;
return timeout ;
}
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 ;
timeout = compute_timeout(sv->aresid, what) ;
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, 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, 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 (info->opt_timeout)
tain_from_millisecs(&deadline, info->timeout) ;
else
deadline = tain_infinite_relative ;
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");
}
tain_now_set_stopwatch_g() ;
tain_add_g(&deadline, &deadline) ;
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 ;
}