Пример #1
0
static int svstatus_get(void)
{
	if ((fd = open_write("supervise/ok")) == -1) {
		if (errno == ENODEV) {
			*acts == 'x' ? ok("runsv not running")
			             : failx("runsv not running");
			return 0;
		}
		warn_cannot("open supervise/ok");
		return -1;
	}
	close(fd);
	if ((fd = open_read("supervise/status")) == -1) {
		warn_cannot("open supervise/status");
		return -1;
	}
	r = read(fd, svstatus, 20);
	close(fd);
	switch (r) {
	case 20: break;
	case -1: warn_cannot("read supervise/status"); return -1;
	default: warnx_cannot("read supervise/status: bad format"); return -1;
	}
	return 1;
}
Пример #2
0
static int control(char *a)
{
	if (svstatus_get() <= 0) return -1;
	if (svstatus[17] == *a) return 0;
	if ((fd = open_write("supervise/control")) == -1) {
		if (errno != ENODEV)
			warn_cannot("open supervise/control");
		else
			*a == 'x' ? ok("runsv not running") : failx("runsv not running");
		return -1;
	}
	r = write(fd, a, strlen(a));
	close(fd);
	if (r != strlen(a)) {
		warn_cannot("write to supervise/control");
		return -1;
	}
	return 1;
}
Пример #3
0
static unsigned custom(struct svdir *s, char c)
{
	int pid;
	int w;
	char a[10];
	struct stat st;
	char *prog[2];

	if (s->islog) return 0;
	strcpy(a, "control/?");
	a[8] = c; /* replace '?' */
	if (stat(a, &st) == 0) {
		if (st.st_mode & S_IXUSR) {
			pid = vfork();
			if (pid == -1) {
				warn_cannot("vfork for control/?");
				return 0;
			}
			if (!pid) {
				/* child */
				if (haslog && dup2(logpipe.wr, 1) == -1)
					warn_cannot("setup stdout for control/?");
				prog[0] = a;
				prog[1] = NULL;
				execv(a, prog);
				fatal_cannot("run control/?");
			}
			/* parent */
			while (safe_waitpid(pid, &w, 0) == -1) {
				warn_cannot("wait for child control/?");
				return 0;
			}
			return !wait_exitcode(w);
		}
	} else {
		if (errno != ENOENT)
			warn_cannot("stat control/?");
	}
	return 0;
}
Пример #4
0
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";
		custom(s, 'u');
	}
	run[1] = NULL;

	if (s->pid != 0)
		stopservice(s); /* should never happen */
	while ((p = vfork()) == -1) {
		warn_cannot("vfork, sleeping");
		sleep(5);
	}
	if (p == 0) {
		/* child */
		if (haslog) {
			/* NB: bug alert! right order is close, then dup2 */
			if (s->islog) {
				xchdir("./log");
				close(logpipe.wr);
				xdup2(logpipe.rd, 0);
			} else {
				close(logpipe.rd);
				xdup2(logpipe.wr, 1);
			}
		}
		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);
	}
	/* parent */
	if (s->state != S_FINISH) {
		gettimeofday_ns(&s->start);
		s->state = S_RUN;
	}
	s->pid = p;
	pidchanged = 1;
	s->ctrl = C_NOOP;
	update_status(s);
}
Пример #5
0
static unsigned custom(struct svdir *s, char c)
{
	pid_t pid;
	int w;
	char a[10];
	struct stat st;

	if (s->islog)
		return 0;
	strcpy(a, "control/?");
	a[8] = c; /* replace '?' */
	if (stat(a, &st) == 0) {
		if (st.st_mode & S_IXUSR) {
			pid = vfork();
			if (pid == -1) {
				warn_cannot("vfork for control/?");
				return 0;
			}
			if (pid == 0) {
				/* child */
				if (haslog && dup2(logpipe.wr, 1) == -1)
					warn_cannot("setup stdout for control/?");
				execl(a, a, (char *) NULL);
				fatal_cannot("run control/?");
			}
			/* parent */
			if (safe_waitpid(pid, &w, 0) == -1) {
				warn_cannot("wait for child control/?");
				return 0;
			}
			return WEXITSTATUS(w) == 0;
		}
	} else {
		if (errno != ENOENT)
			warn_cannot("stat control/?");
	}
	return 0;
}
Пример #6
0
int runsv_main(int argc UNUSED_PARAM, char **argv)
{
	struct stat s;
	int fd;
	int r;
	char buf[256];

	INIT_G();

	dir = single_argv(argv);

	xpiped_pair(selfpipe);
	close_on_exec_on(selfpipe.rd);
	close_on_exec_on(selfpipe.wr);
	ndelay_on(selfpipe.rd);
	ndelay_on(selfpipe.wr);

	sig_block(SIGCHLD);
	bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
	sig_block(SIGTERM);
	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].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].sd_want = W_DOWN;

	if (stat("log", &s) == -1) {
		if (errno != ENOENT)
			warn_cannot("stat ./log");
	} else {
		if (!S_ISDIR(s.st_mode)) {
			errno = 0;
			warn_cannot("stat log/down: log is not a directory");
		} else {
			haslog = 1;
			svd[1].state = S_DOWN;
			svd[1].ctrl = C_NOOP;
			svd[1].sd_want = W_UP;
			svd[1].islog = 1;
			gettimeofday_ns(&svd[1].start);
			if (stat("log/down", &s) != -1)
				svd[1].sd_want = W_DOWN;
			xpiped_pair(logpipe);
			close_on_exec_on(logpipe.rd);
			close_on_exec_on(logpipe.wr);
		}
	}

	if (mkdir("supervise", 0700) == -1) {
		r = readlink("supervise", buf, sizeof(buf));
		if (r != -1) {
			if (r == sizeof(buf))
				fatal2x_cannot("readlink ./supervise", ": name too long");
			buf[r] = 0;
			mkdir(buf, 0700);
		} else {
			if ((errno != ENOENT) && (errno != EINVAL))
				fatal_cannot("readlink ./supervise");
		}
	}
	svd[0].fdlock = xopen3("log/supervise/lock"+4,
			O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
	if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
		fatal_cannot("lock supervise/lock");
	close_on_exec_on(svd[0].fdlock);
	if (haslog) {
		if (mkdir("log/supervise", 0700) == -1) {
			r = readlink("log/supervise", buf, 256);
			if (r != -1) {
				if (r == 256)
					fatal2x_cannot("readlink ./log/supervise", ": name too long");
				buf[r] = 0;
				fd = xopen(".", O_RDONLY|O_NDELAY);
				xchdir("./log");
				mkdir(buf, 0700);
				if (fchdir(fd) == -1)
					fatal_cannot("change back to service directory");
				close(fd);
			}
			else {
				if ((errno != ENOENT) && (errno != EINVAL))
					fatal_cannot("readlink ./log/supervise");
			}
		}
		svd[1].fdlock = xopen3("log/supervise/lock",
				O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
		if (flock(svd[1].fdlock, LOCK_EX) == -1)
			fatal_cannot("lock log/supervise/lock");
		close_on_exec_on(svd[1].fdlock);
	}

	mkfifo("log/supervise/control"+4, 0600);
	svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
	close_on_exec_on(svd[0].fdcontrol);
	svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
	close_on_exec_on(svd[0].fdcontrolwrite);
	update_status(&svd[0]);
	if (haslog) {
		mkfifo("log/supervise/control", 0600);
		svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
		close_on_exec_on(svd[1].fdcontrol);
		svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
		close_on_exec_on(svd[1].fdcontrolwrite);
		update_status(&svd[1]);
	}
	mkfifo("log/supervise/ok"+4, 0600);
	fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
	close_on_exec_on(fd);
	if (haslog) {
		mkfifo("log/supervise/ok", 0600);
		fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
		close_on_exec_on(fd);
	}
	for (;;) {
		struct pollfd x[3];
		unsigned deadline;
		char ch;

		if (haslog)
			if (!svd[1].pid && svd[1].sd_want == W_UP)
				startservice(&svd[1]);
		if (!svd[0].pid)
			if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
				startservice(&svd[0]);

		x[0].fd = selfpipe.rd;
		x[0].events = POLLIN;
		x[1].fd = svd[0].fdcontrol;
		x[1].events = POLLIN;
		/* x[2] is used only if haslog == 1 */
		x[2].fd = svd[1].fdcontrol;
		x[2].events = POLLIN;
		sig_unblock(SIGTERM);
		sig_unblock(SIGCHLD);
		poll(x, 2 + haslog, 3600*1000);
		sig_block(SIGTERM);
		sig_block(SIGCHLD);

		while (read(selfpipe.rd, &ch, 1) == 1)
			continue;

		for (;;) {
			pid_t child;
			int wstat;

			child = wait_any_nohang(&wstat);
			if (!child)
				break;
			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;
				if (svd[0].state != S_FINISH) {
					fd = open("finish", O_RDONLY|O_NDELAY);
					if (fd != -1) {
						close(fd);
						svd[0].state = S_FINISH;
						update_status(&svd[0]);
						continue;
					}
				}
				svd[0].state = S_DOWN;
				deadline = svd[0].start.tv_sec + 1;
				gettimeofday_ns(&svd[0].start);
				update_status(&svd[0]);
				if (LESS(svd[0].start.tv_sec, deadline))
					sleep(1);
			}
			if (haslog) {
				if (child == svd[1].pid) {
					svd[0].wstat = wstat;
					svd[1].pid = 0;
					pidchanged = 1;
					svd[1].state = S_DOWN;
					svd[1].ctrl &= ~C_TERM;
					deadline = svd[1].start.tv_sec + 1;
					gettimeofday_ns(&svd[1].start);
					update_status(&svd[1]);
					if (LESS(svd[1].start.tv_sec, deadline))
						sleep(1);
				}
			}
		} /* for (;;) */
		if (read(svd[0].fdcontrol, &ch, 1) == 1)
			ctrl(&svd[0], ch);
		if (haslog)
			if (read(svd[1].fdcontrol, &ch, 1) == 1)
				ctrl(&svd[1], ch);

		if (sigterm) {
			ctrl(&svd[0], 'x');
			sigterm = 0;
		}

		if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
			if (svd[1].pid == 0)
				_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);
				close(logpipe.rd);
			}
		}
	} /* for (;;) */
	/* not reached */
	return 0;
}
Пример #7
0
static void startservice(struct svdir *s)
{
	int p;
	const char *arg[4];
	char exitcode[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)) {
			*utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
			arg[1] = exitcode;
		}
		//arg[2] = "0";
		//if (WIFSIGNALED(s->wstat)) {
			arg[2] = utoa(WTERMSIG(s->wstat));
		//}
		arg[3] = NULL;
	} else {
		arg[0] = "./run";
		arg[1] = NULL;
		custom(s, 'u');
	}

	if (s->pid != 0)
		stopservice(s); /* should never happen */
	while ((p = vfork()) == -1) {
		warn_cannot("vfork, sleeping");
		sleep(5);
	}
	if (p == 0) {
		/* child */
		if (haslog) {
			/* NB: bug alert! right order is close, then dup2 */
			if (s->islog) {
				xchdir("./log");
				close(logpipe.wr);
				xdup2(logpipe.rd, 0);
			} else {
				close(logpipe.rd);
				xdup2(logpipe.wr, 1);
			}
		}
		/* 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);
		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;
	}
	s->pid = p;
	pidchanged = 1;
	s->ctrl = C_NOOP;
	update_status(s);
}
Пример #8
0
static void update_status(struct svdir *s)
{
	ssize_t sz;
	int fd;
	svstatus_t status;

	/* pid */
	if (pidchanged) {
		fd = open_trunc_or_warn("supervise/pid.new");
		if (fd < 0)
			return;
		if (s->pid) {
			char spid[sizeof(int)*3 + 2];
			int size = sprintf(spid, "%u\n", (unsigned)s->pid);
			write(fd, spid, size);
		}
		close(fd);
		if (rename_or_warn("supervise/pid.new",
				s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
			return;
		pidchanged = 0;
	}

	/* stat */
	fd = open_trunc_or_warn("supervise/stat.new");
	if (fd < -1)
		return;

	{
		char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
		char *p = stat_buf;
		switch (s->state) {
		case S_DOWN:
			p = stpcpy(p, "down");
			break;
		case S_RUN:
			p = stpcpy(p, "run");
			break;
		case S_FINISH:
			p = stpcpy(p, "finish");
			break;
		}
		if (s->ctrl & C_PAUSE)
			p = stpcpy(p, ", paused");
		if (s->ctrl & C_TERM)
			p = stpcpy(p, ", got TERM");
		if (s->state != S_DOWN)
			switch (s->sd_want) {
			case W_DOWN:
				p = stpcpy(p, ", want down");
				break;
			case W_EXIT:
				p = stpcpy(p, ", want exit");
				break;
			}
		*p++ = '\n';
		write(fd, stat_buf, p - stat_buf);
		close(fd);
	}

	rename_or_warn("supervise/stat.new",
		s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);

	/* supervise compatibility */
	memset(&status, 0, sizeof(status));
	status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
	status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
	status.pid_le32 = SWAP_LE32(s->pid);
	if (s->ctrl & C_PAUSE)
		status.paused = 1;
	if (s->sd_want == W_UP)
		status.want = 'u';
	else
		status.want = 'd';
	if (s->ctrl & C_TERM)
		status.got_term = 1;
	status.run_or_finish = s->state;
	fd = open_trunc_or_warn("supervise/status.new");
	if (fd < 0)
		return;
	sz = write(fd, &status, sizeof(status));
	close(fd);
	if (sz != sizeof(status)) {
		warn_cannot("write supervise/status.new");
		unlink("supervise/status.new");
		return;
	}
	rename_or_warn("supervise/status.new",
		s->islog ? "log/supervise/status" : "log/supervise/status"+4);
}
Пример #9
0
static void warnx_cannot(char *m1) { errno = 0; warn_cannot(m1); }