/* perpd_svdef_run() ** exec() a service: ** "which" is SUBSV_MAIN or SUBSV_LOG ** "target" is SVRUN_START or SVRUN_RESET ** ** side effects: ** global flag_failing set on fail of fork() ** child uses (but does not alter) global poll_sigset */ int perpd_svdef_run(struct svdef *svdef, int which, int target) { struct subsv *subsv = &svdef->svpair[which]; char *prog[7]; tain_t now, when_ok; tain_t towait = tain_INIT(0,0); pid_t pid; int wstat; char nbuf_reset[NFMT_SIZE]; int i; /* insanity checks: */ if((which == SUBSV_LOG) && !(svdef->bitflags & SVDEF_FLAG_HASLOG)){ log_debug("logging service requested but not enabled"); return 0; } if(subsv->pid > 0){ log_debug("perpd_svrun() requested for service still running"); return 0; } /* initialize (attempted) target, etc: */ switch(target){ case SVRUN_RESET: subsv->bitflags |= SUBSV_FLAG_ISRESET; break; default: subsv->bitflags &= ~SUBSV_FLAG_ISRESET; break; } wstat = subsv->wstat; subsv->pid = 0; subsv->wstat = 0; subsv->bitflags &= ~SUBSV_FLAG_FAILING; /* setup argv: */ prog[0] = (which == SUBSV_LOG) ? "./rc.log" : "./rc.main"; prog[1] = (target == SVRUN_START) ? "start" : "reset"; prog[2] = svdef->name; prog[3] = NULL; /* additional args if running "reset": */ if(target == SVRUN_RESET){ if(WIFEXITED(wstat)){ prog[3] = "exit"; prog[4] = nfmt_uint32(nbuf_reset, (uint32_t)WEXITSTATUS(wstat)); prog[5] = NULL; } else { int n = (WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WSTOPSIG(wstat)); char *s = (char *)sysstr_signal(n); prog[3] = (WIFSIGNALED(wstat) ? "signal" : "stopped"); prog[4] = nfmt_uint32(nbuf_reset, (uint32_t)n); prog[5] = ((s != NULL) ? s : "SIGUNKNOWN"); prog[6] = NULL; } } /* timestamps and respawn governor: */ tain_now(&now); tain_assign(&when_ok, &subsv->when_ok); if((target == SVRUN_START) && tain_less(&now, &when_ok)){ log_warning("setting respawn governor on 'start' target of service ", svdef->name, " for ", prog[0]); tain_minus(&towait, &when_ok, &now); } /* fork/exec: */ if((pid = fork()) == -1){ subsv->pid = 0; subsv->bitflags |= SUBSV_FLAG_FAILING; perpd_trigger_fail(); warn_syserr("failure fork() for service ", svdef->name); return -1; } /* XXX, TODO: ** if child error before exec(), die() with a distinctive error code */ /* child: */ if(pid == 0){ /* nfmt buffer for environmental variables (reusable with newenv_set()): */ char nbuf_env[NFMT_SIZE]; /* run child in new process group: */ setsid(); /* cwd for runscripts is svdir: */ if(fchdir(svdef->fd_dir) == -1){ fatal_syserr("(in child for service ", svdef->name, "): failure fchdir() to service directory"); } /* setup logpipe: */ if(svdef->bitflags & SVDEF_FLAG_HASLOG){ if(which == SUBSV_MAIN){ /* set stdout to logpipe: */ close(1); if(dup2(svdef->logpipe[1], 1) != 1){ fatal_syserr("(in child for service ", svdef->name, "): failure dup2() on logpipe[1] to logging service"); } } if((which == SUBSV_LOG) && (target == SVRUN_START)){ /* set stdin to logpipe: ** (but not if this is a resetting log service) */ close(0); if(dup2(svdef->logpipe[0], 0) != 0){ fatal_syserr("(in child for service ", svdef->name, "): failure dup2() on logpipe[0] for logging service"); } } close(svdef->logpipe[0]); close(svdef->logpipe[1]); } /* close extraneous descriptors (shouldn't be any!): */ for(i = 3; i < 1024; ++i) close(i); /* set PERP_BASE in the environment: */ if(newenv_set("PERP_BASE", basedir) == -1){ fatal_syserr("(in child for service ", svdef->name, "): failure setting PERP_BASE environment for ", prog[0], " ", prog[1]); } /* set PERP_SVPID in the environment: */ if(target == SVRUN_RESET){ nfmt_uint64(nbuf_env, (uint64_t)subsv->pid_prev); }else{ nfmt_uint64(nbuf_env, (uint64_t)getpid()); } if(newenv_set("PERP_SVPID", nbuf_env) == -1){ fatal_syserr("(in child for service ", svdef->name, "): failure setting PERP_SVPID environment for ", prog[0], " ", prog[1]); } /* set PERP_SVSECS in the environment (reset target only): */ if(target == SVRUN_RESET){ nfmt_uint64(nbuf_env, tain_uptime(&now, &subsv->when)); if(newenv_set("PERP_SVSECS", nbuf_env) == -1){ fatal_syserr("(in child for service ", svdef->name, "): failure setting PERP_SVSECS environment for ", prog[0], " ", prog[1]); } } /* respawn governor: */ if((target == SVRUN_START) && !(tain_iszero(&towait))){ tain_pause(&towait, NULL); } /* clear signal handlers from child process: */ sig_uncatch(SIGCHLD); sig_uncatch(SIGHUP); sig_uncatch(SIGINT); sig_uncatch(SIGTERM); sig_uncatch(SIGPIPE); sigset_unblock(&poll_sigset); /* go forth my child: */ newenv_run(prog, environ); /* nuts, exec failed: */ fatal_syserr("(in child for service ", svdef->name, "): failure execve()"); } /* parent: */ subsv->pid = pid; /* set timestamps and respawn governor: */ tain_assign(&subsv->when, &now); if(target == SVRUN_START){ /* when_ok = now + 1sec + wait: */ tain_LOAD(&when_ok, 1, 0); tain_plus(&when_ok, &now, &when_ok); tain_plus(&when_ok, &when_ok, &towait); tain_assign(&subsv->when_ok, &when_ok); } return 0; }
static void report(const char *name, const uchar_t *status, const tain_t *now) { pid_t pid; tain_t when; uint64_t uptime; uchar_t flags; int haslog; char nbuf[NFMT_SIZE]; /* svdef: */ tain_unpack(&when, &status[16]); flags = status[28]; haslog = (flags & SVDEF_FLAG_HASLOG) ? 1 : 0; /* uptime since activation: */ uptime = tain_uptime(now, &when); vputs(name, ": activated ", nfmt_uint64(nbuf, uptime), " seconds"); if(flags & SVDEF_FLAG_CULL){ vputs(", deactivation in progress"); } if(flags & SVDEF_FLAG_CYCLE){ vputs(", flagged for reactivation"); } vputs("\n"); /* main: */ pid = upak32_unpack(&status[30]); tain_unpack(&when, &status[34]); flags = status[46]; uptime = tain_uptime(now, &when); vputs(" main: "); if(pid == 0){ vputs("down ", nfmt_uint64(nbuf, uptime), " seconds"); if(!(flags & SUBSV_FLAG_WANTDOWN)) vputs(", want up!"); if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once"); }else if((uptime < 1) && !(flags & SUBSV_FLAG_ISRESET)){ /* munge uptime < 1 second to want up: */ vputs("down, want up!"); if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once"); }else{ vputs((flags & SUBSV_FLAG_ISRESET) ? "resetting " : "up "); vputs(nfmt_uint64(nbuf, uptime), " seconds"); vputs(" (pid ", nfmt_uint32(nbuf, (uint32_t)pid), ")"); if(flags & SUBSV_FLAG_ISPAUSED) vputs(", paused"); if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once"); } /* log: */ vputs("\n log: "); if(!haslog){ vputs("(no log)\n"); return; } /* else: */ pid = upak32_unpack(&status[48]); tain_unpack(&when, &status[52]); flags = status[64]; uptime = tain_uptime(now, &when); if(pid == 0){ vputs("down ", nfmt_uint64(nbuf, uptime), " seconds"); if(!(flags & SUBSV_FLAG_WANTDOWN)) vputs(", want up!"); if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once"); }else if((uptime < 1) && !(flags & SUBSV_FLAG_ISRESET)){ /* munge uptime < 1 second to want up: */ vputs("down, want up!"); if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once"); }else{ vputs((flags & SUBSV_FLAG_ISRESET) ? "resetting " : "up "); vputs(nfmt_uint64(nbuf, uptime), " seconds"); vputs(" (pid ", nfmt_uint32(nbuf, (uint32_t)pid), ")"); if(flags & SUBSV_FLAG_ISPAUSED) vputs(", paused"); if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once"); } vputs("\n"); return; }