Esempio n. 1
0
void check_errors_in_children(int signo)
{
	int status;

	if (!signo) {
		/* block waiting for any child */
		if (wait(&status) < 0)
//FIXME: check EINTR?
			return; /* probably there are no children */
		goto check_status;
	}

	/* Wait for any child without blocking */
	for (;;) {
		if (wait_any_nohang(&status) < 0)
//FIXME: check EINTR?
			/* wait failed?! I'm confused... */
			return;
 check_status:
		/*if (WIFEXITED(status) && WEXITSTATUS(status) == 0)*/
		/* On Linux, the above can be checked simply as: */
		if (status == 0)
			/* this child exited with 0 */
			continue;
		/* Cannot happen:
		if (!WIFSIGNALED(status) && !WIFEXITED(status)) ???;
		 */
		bb_got_signal = 1;
	}
}
Esempio n. 2
0
static void handle_SIGCHLD(int status)
{
	/* Actually, 'status' is a signo. We reuse it for other needs */

	/* Wait for any child without blocking */
	if (wait_any_nohang(&status) < 0)
		/* wait failed?! I'm confused... */
		return;

	if (WIFEXITED(status) && WEXITSTATUS(status)==0)
		/* child exited with 0 */
		return;
	/* Cannot happen?
	if (!WIFSIGNALED(status) && !WIFEXITED(status)) return; */
	child_error = 1;
}
Esempio n. 3
0
static void sig_child_handler(int sig ATTRIBUTE_UNUSED)
{
	int wstat;
	int pid;

	while ((pid = wait_any_nohang(&wstat)) > 0) {
		if (max_per_host)
			ipsvd_perhost_remove(pid);
		if (cnum)
			cnum--;
		if (verbose)
			print_waitstat(pid, wstat);
	}
	if (verbose)
		connection_status();
}
Esempio n. 4
0
int runsvdir_main(int argc UNUSED_PARAM, char **argv)
{
	struct stat s;
	dev_t last_dev = last_dev; /* for gcc */
	ino_t last_ino = last_ino; /* for gcc */
	time_t last_mtime = 0;
	int wstat;
	int curdir;
	pid_t pid;
	unsigned deadline;
	unsigned now;
	unsigned stampcheck;
	int i;
	int need_rescan = 1;
	char *opt_s_argv[3];

	INIT_G();

	opt_complementary = "-1";
	opt_s_argv[0] = NULL;
	opt_s_argv[2] = NULL;
	getopt32(argv, "Ps:", &opt_s_argv[0]);
	argv += optind;

	bb_signals(0
		| (1 << SIGTERM)
		| (1 << SIGHUP)
		/* For busybox's init, SIGTERM == reboot,
		 * SIGUSR1 == halt
		 * SIGUSR2 == poweroff
		 * so we need to intercept SIGUSRn too.
		 * Note that we do not implement actual reboot
		 * (killall(TERM) + umount, etc), we just pause
		 * respawing and avoid exiting (-> making kernel oops).
		 * The user is responsible for the rest. */
		| (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
		, record_signo);
	svdir = *argv++;

#if ENABLE_FEATURE_RUNSVDIR_LOG
	/* setup log */
	if (*argv) {
		rplog = *argv;
		rploglen = strlen(rplog);
		if (rploglen < 7) {
			warnx("log must have at least seven characters");
		} else if (piped_pair(logpipe)) {
			warnx("can't create pipe for log");
		} else {
			close_on_exec_on(logpipe.rd);
			close_on_exec_on(logpipe.wr);
			ndelay_on(logpipe.rd);
			ndelay_on(logpipe.wr);
			if (dup2(logpipe.wr, 2) == -1) {
				warnx("can't set filedescriptor for log");
			} else {
				pfd[0].fd = logpipe.rd;
				pfd[0].events = POLLIN;
				stamplog = monotonic_sec();
				goto run;
			}
		}
		rplog = NULL;
		warnx("log service disabled");
	}
 run:
#endif
	curdir = open(".", O_RDONLY|O_NDELAY);
	if (curdir == -1)
		fatal2_cannot("open current directory", "");
	close_on_exec_on(curdir);

	stampcheck = monotonic_sec();

	for (;;) {
		/* collect children */
		for (;;) {
			pid = wait_any_nohang(&wstat);
			if (pid <= 0)
				break;
			for (i = 0; i < svnum; i++) {
				if (pid == sv[i].pid) {
					/* runsv has died */
					sv[i].pid = 0;
					need_rescan = 1;
				}
			}
		}

		now = monotonic_sec();
		if ((int)(now - stampcheck) >= 0) {
			/* wait at least a second */
			stampcheck = now + 1;

			if (stat(svdir, &s) != -1) {
				if (need_rescan || s.st_mtime != last_mtime
				 || s.st_ino != last_ino || s.st_dev != last_dev
				) {
					/* svdir modified */
					if (chdir(svdir) != -1) {
						last_mtime = s.st_mtime;
						last_dev = s.st_dev;
						last_ino = s.st_ino;
						/* if the svdir changed this very second, wait until the
						 * next second, because we won't be able to detect more
						 * changes within this second */
						while (time(NULL) == last_mtime)
							usleep(100000);
						need_rescan = do_rescan();
						while (fchdir(curdir) == -1) {
							warn2_cannot("change directory, pausing", "");
							sleep(5);
						}
					} else {
						warn2_cannot("change directory to ", svdir);
					}
				}
			} else {
				warn2_cannot("stat ", svdir);
			}
		}

#if ENABLE_FEATURE_RUNSVDIR_LOG
		if (rplog) {
			if ((int)(now - stamplog) >= 0) {
				write(logpipe.wr, ".", 1);
				stamplog = now + 900;
			}
		}
		pfd[0].revents = 0;
#endif
		deadline = (need_rescan ? 1 : 5);
		sig_block(SIGCHLD);
#if ENABLE_FEATURE_RUNSVDIR_LOG
		if (rplog)
			poll(pfd, 1, deadline*1000);
		else
#endif
			sleep(deadline);
		sig_unblock(SIGCHLD);

#if ENABLE_FEATURE_RUNSVDIR_LOG
		if (pfd[0].revents & POLLIN) {
			char ch;
			while (read(logpipe.rd, &ch, 1) > 0) {
				if (ch < ' ')
					ch = ' ';
				for (i = 6; i < rploglen; i++)
					rplog[i-1] = rplog[i];
				rplog[rploglen-1] = ch;
			}
		}
#endif
		if (!bb_got_signal)
			continue;

		/* -s SCRIPT: useful if we are init.
		 * In this case typically script never returns,
		 * it halts/powers off/reboots the system. */
		if (opt_s_argv[0]) {
			/* Single parameter: signal# */
			opt_s_argv[1] = utoa(bb_got_signal);
			pid = spawn(opt_s_argv);
			if (pid > 0) {
				/* Remembering to wait for _any_ children,
				 * not just pid */
				while (wait(NULL) != pid)
					continue;
			}
		}

		if (bb_got_signal == SIGHUP) {
			for (i = 0; i < svnum; i++)
				if (sv[i].pid)
					kill(sv[i].pid, SIGTERM);
		}
		/* SIGHUP or SIGTERM (or SIGUSRn if we are init) */
		/* Exit unless we are init */
		if (getpid() != 1)
			return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;

		/* init continues to monitor services forever */
		bb_got_signal = 0;
	} /* for (;;) */
}
Esempio n. 5
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;
}