ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
+/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
/* TODO: depends on runit_lib.c - review and reduce/eliminate */
#include <sys/poll.h>
int pid;
smallint state;
smallint ctrl;
- smallint want;
+ smallint sd_want;
smallint islog;
struct timespec start;
int fdlock;
int fdcontrol;
int fdcontrolwrite;
+ int wstat;
};
struct globals {
#define logpipe (G.logpipe )
#define dir (G.dir )
#define svd (G.svd )
-#define INIT_G() \
- do { \
- pidchanged = 1; \
- } while (0)
+#define INIT_G() do { \
+ pidchanged = 1; \
+} while (0)
static void fatal2_cannot(const char *m1, const char *m2)
{
bb_perror_msg("%s: warning: cannot %s", dir, m);
}
-static void s_child(int sig_no)
+static void s_child(int sig_no UNUSED_PARAM)
{
write(selfpipe.wr, "", 1);
}
-static void s_term(int sig_no)
+static void s_term(int sig_no UNUSED_PARAM)
{
sigterm = 1;
write(selfpipe.wr, "", 1); /* XXX */
}
-static char *add_str(char *p, const char *to_add)
+/* libbb candidate */
+static char *bb_stpcpy(char *p, const char *to_add)
{
while ((*p = *to_add) != '\0') {
p++;
return fd;
}
-static int rename_or_warn(const char *old, const char *new)
-{
- if (rename(old, new) == -1) {
- bb_perror_msg("%s: warning: cannot rename %s to %s",
- dir, old, new);
- return -1;
- }
- return 0;
-}
-
static void update_status(struct svdir *s)
{
ssize_t sz;
char *p = stat_buf;
switch (s->state) {
case S_DOWN:
- p = add_str(p, "down");
+ p = bb_stpcpy(p, "down");
break;
case S_RUN:
- p = add_str(p, "run");
+ p = bb_stpcpy(p, "run");
break;
case S_FINISH:
- p = add_str(p, "finish");
+ p = bb_stpcpy(p, "finish");
break;
}
- if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
- if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
+ if (s->ctrl & C_PAUSE)
+ p = bb_stpcpy(p, ", paused");
+ if (s->ctrl & C_TERM)
+ p = bb_stpcpy(p, ", got TERM");
if (s->state != S_DOWN)
- switch (s->want) {
+ switch (s->sd_want) {
case W_DOWN:
- p = add_str(p, ", want down");
+ p = bb_stpcpy(p, ", want down");
break;
case W_EXIT:
- p = add_str(p, ", want exit");
+ p = bb_stpcpy(p, ", want exit");
break;
}
*p++ = '\n';
status.pid_le32 = SWAP_LE32(s->pid);
if (s->ctrl & C_PAUSE)
status.paused = 1;
- if (s->want == W_UP)
+ if (s->sd_want == W_UP)
status.want = 'u';
else
status.want = 'd';
static unsigned custom(struct svdir *s, char c)
{
- int pid;
+ pid_t pid;
int w;
char a[10];
struct stat st;
- char *prog[2];
- if (s->islog) return 0;
+ if (s->islog)
+ return 0;
strcpy(a, "control/?");
- a[8] = c;
+ a[8] = c; /* replace '?' */
if (stat(a, &st) == 0) {
if (st.st_mode & S_IXUSR) {
- pid = fork();
+ pid = vfork();
if (pid == -1) {
- warn_cannot("fork for control/?");
+ warn_cannot("vfork for control/?");
return 0;
}
- if (!pid) {
+ if (pid == 0) {
+ /* child */
if (haslog && dup2(logpipe.wr, 1) == -1)
warn_cannot("setup stdout for control/?");
- prog[0] = a;
- prog[1] = NULL;
- execv(a, prog);
+ execl(a, a, (char *) NULL);
fatal_cannot("run control/?");
}
- while (safe_waitpid(pid, &w, 0) == -1) {
+ /* parent */
+ if (safe_waitpid(pid, &w, 0) == -1) {
warn_cannot("wait for child control/?");
return 0;
}
- return !wait_exitcode(w);
+ return WEXITSTATUS(w) == 0;
}
} else {
if (errno != ENOENT)
s->ctrl |= C_TERM;
update_status(s);
}
- if (s->want == W_DOWN) {
+ if (s->sd_want == W_DOWN) {
kill(s->pid, SIGCONT);
custom(s, 'd');
return;
}
- if (s->want == W_EXIT) {
+ if (s->sd_want == W_EXIT) {
kill(s->pid, SIGCONT);
custom(s, 'x');
}
static void startservice(struct svdir *s)
{
int p;
- char *run[2];
-
- if (s->state == S_FINISH)
- run[0] = (char*)"./finish";
- else {
- run[0] = (char*)"./run";
+ const char *arg[4];
+ char exitcode[sizeof(int)*3 + 2];
+ char sigcode[sizeof(int)*3 + 2];
+
+ if (s->state == S_FINISH) {
+/* Two arguments are given to ./finish. The first one is ./run exit code,
+ * or -1 if ./run didnt exit normally. The second one is
+ * the least significant byte of the exit status as determined by waitpid;
+ * for instance it is 0 if ./run exited normally, and the signal number
+ * if ./run was terminated by a signal. If runsv cannot start ./run
+ * for some reason, the exit code is 111 and the status is 0.
+ */
+ arg[0] = "./finish";
+ arg[1] = "-1";
+ if (WIFEXITED(s->wstat)) {
+ sprintf(exitcode, "%u", (int) WEXITSTATUS(s->wstat));
+ arg[1] = exitcode;
+ }
+ //arg[2] = "0";
+ //if (WIFSIGNALED(s->wstat)) {
+ sprintf(sigcode, "%u", (int) WTERMSIG(s->wstat));
+ arg[2] = sigcode;
+ //}
+ arg[3] = NULL;
+ } else {
+ arg[0] = "./run";
+ arg[1] = NULL;
custom(s, 'u');
}
- run[1] = NULL;
if (s->pid != 0)
stopservice(s); /* should never happen */
- while ((p = fork()) == -1) {
- warn_cannot("fork, sleeping");
+ while ((p = vfork()) == -1) {
+ warn_cannot("vfork, sleeping");
sleep(5);
}
if (p == 0) {
xdup2(logpipe.wr, 1);
}
}
- signal(SIGCHLD, SIG_DFL);
- signal(SIGTERM, SIG_DFL);
+ /* Non-ignored signals revert to SIG_DFL on exec anyway */
+ /*bb_signals(0
+ + (1 << SIGCHLD)
+ + (1 << SIGTERM)
+ , SIG_DFL);*/
sig_unblock(SIGCHLD);
sig_unblock(SIGTERM);
- execvp(*run, run);
- fatal2_cannot(s->islog ? "start log/" : "start ", *run);
+ execv(arg[0], (char**) arg);
+ fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
}
+ /* parent */
if (s->state != S_FINISH) {
gettimeofday_ns(&s->start);
s->state = S_RUN;
switch (c) {
case 'd': /* down */
- s->want = W_DOWN;
+ s->sd_want = W_DOWN;
update_status(s);
if (s->pid && s->state != S_FINISH)
stopservice(s);
break;
case 'u': /* up */
- s->want = W_UP;
+ s->sd_want = W_UP;
update_status(s);
if (s->pid == 0)
startservice(s);
case 'x': /* exit */
if (s->islog)
break;
- s->want = W_EXIT;
+ s->sd_want = W_EXIT;
update_status(s);
/* FALLTHROUGH */
case 't': /* sig term */
case 'c': /* sig cont */
if (s->pid && !custom(s, c))
kill(s->pid, SIGCONT);
- if (s->ctrl & C_PAUSE)
- s->ctrl &= ~C_PAUSE;
+ s->ctrl &= ~C_PAUSE;
update_status(s);
break;
case 'o': /* once */
- s->want = W_DOWN;
+ s->sd_want = W_DOWN;
update_status(s);
if (!s->pid)
startservice(s);
}
int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int runsv_main(int argc, char **argv)
+int runsv_main(int argc UNUSED_PARAM, char **argv)
{
struct stat s;
int fd;
ndelay_on(selfpipe.wr);
sig_block(SIGCHLD);
- sig_catch(SIGCHLD, s_child);
+ bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
sig_block(SIGTERM);
- sig_catch(SIGTERM, s_term);
+ bb_signals_recursive_norestart(1 << SIGTERM, s_term);
xchdir(dir);
/* bss: svd[0].pid = 0; */
if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
if (C_NOOP) svd[0].ctrl = C_NOOP;
- if (W_UP) svd[0].want = W_UP;
+ if (W_UP) svd[0].sd_want = W_UP;
/* bss: svd[0].islog = 0; */
/* bss: svd[1].pid = 0; */
gettimeofday_ns(&svd[0].start);
- if (stat("down", &s) != -1) svd[0].want = W_DOWN;
+ if (stat("down", &s) != -1)
+ svd[0].sd_want = W_DOWN;
if (stat("log", &s) == -1) {
if (errno != ENOENT)
haslog = 1;
svd[1].state = S_DOWN;
svd[1].ctrl = C_NOOP;
- svd[1].want = W_UP;
+ svd[1].sd_want = W_UP;
svd[1].islog = 1;
gettimeofday_ns(&svd[1].start);
if (stat("log/down", &s) != -1)
- svd[1].want = W_DOWN;
+ svd[1].sd_want = W_DOWN;
xpiped_pair(logpipe);
close_on_exec_on(logpipe.rd);
close_on_exec_on(logpipe.wr);
char ch;
if (haslog)
- if (!svd[1].pid && svd[1].want == W_UP)
+ if (!svd[1].pid && svd[1].sd_want == W_UP)
startservice(&svd[1]);
if (!svd[0].pid)
- if (svd[0].want == W_UP || svd[0].state == S_FINISH)
+ if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
startservice(&svd[0]);
x[0].fd = selfpipe.rd;
continue;
for (;;) {
- int child;
+ pid_t child;
int wstat;
child = wait_any_nohang(&wstat);
if ((child == -1) && (errno != EINTR))
break;
if (child == svd[0].pid) {
+ svd[0].wstat = wstat;
svd[0].pid = 0;
pidchanged = 1;
- svd[0].ctrl &=~ C_TERM;
+ svd[0].ctrl &= ~C_TERM;
if (svd[0].state != S_FINISH) {
fd = open_read("finish");
if (fd != -1) {
}
if (haslog) {
if (child == svd[1].pid) {
+ svd[0].wstat = wstat;
svd[1].pid = 0;
pidchanged = 1;
svd[1].state = S_DOWN;
sigterm = 0;
}
- if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
+ if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
if (svd[1].pid == 0)
- _exit(0);
- if (svd[1].want != W_EXIT) {
- svd[1].want = W_EXIT;
+ _exit(EXIT_SUCCESS);
+ if (svd[1].sd_want != W_EXIT) {
+ svd[1].sd_want = W_EXIT;
/* stopservice(&svd[1]); */
update_status(&svd[1]);
close(logpipe.wr);