Ejemplo n.º 1
0
static void runsv(int no, const char *name)
{
	pid_t pid;
	char *prog[3];

	prog[0] = (char*)"runsv";
	prog[1] = (char*)name;
	prog[2] = NULL;

	pid = vfork();

	if (pid == -1) {
		warn2_cannot("vfork", "");
		return;
	}
	if (pid == 0) {
		/* child */
		if (set_pgrp)
			setsid();
		signal(SIGHUP, SIG_DFL);
		signal(SIGTERM, SIG_DFL);
		execvp(prog[0], prog);
		fatal2_cannot("start runsv ", name);
	}
	sv[no].pid = pid;
}
Ejemplo n.º 2
0
/* inlining + vfork -> bigger code */
static NOINLINE pid_t runsv(const char *name)
{
	pid_t pid;

	/* If we got signaled, stop spawning children at once! */
	if (bb_got_signal)
		return 0;

	pid = vfork();
	if (pid == -1) {
		warn2_cannot("vfork", "");
		return 0;
	}
	if (pid == 0) {
		/* child */
		if (option_mask32 & 1) /* -P option? */
			setsid();
/* man execv:
 * "Signals set to be caught by the calling process image
 *  shall be set to the default action in the new process image."
 * Therefore, we do not need this: */
#if 0
		bb_signals(0
			| (1 << SIGHUP)
			| (1 << SIGTERM)
			, SIG_DFL);
#endif
		execlp("runsv", "runsv", name, (char *) NULL);
		fatal2_cannot("start runsv ", name);
	}
	return pid;
}
Ejemplo n.º 3
0
static void runsv(int no, const char *name)
{
	int pid = fork();

	if (pid == -1) {
		warn2_cannot("fork for ", name);
		return;
	}
	if (pid == 0) {
		/* child */
		char *prog[3];

		prog[0] = (char*)"runsv";
		prog[1] = (char*)name;
		prog[2] = NULL;
		if (pgrp)
			setsid();
		signal(SIGHUP, SIG_DFL);
		signal(SIGTERM, SIG_DFL);
		BB_EXECVP(prog[0], prog);
		//pathexec_run(*prog, prog, (char* const*)environ);
		fatal2_cannot("start runsv ", name);
	}
	sv[no].pid = pid;
}
Ejemplo n.º 4
0
int runsvdir_main(int argc, 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;
	int pid;
	unsigned deadline;
	unsigned now;
	unsigned stampcheck;
	char ch;
	int i;

	argv++;
	if (!*argv)
		bb_show_usage();
	if (argv[0][0] == '-') {
		switch (argv[0][1]) {
		case 'P': set_pgrp = 1;
		case '-': ++argv;
		}
		if (!*argv)
			bb_show_usage();
	}

	sig_catch(SIGTERM, s_term);
	sig_catch(SIGHUP, s_hangup);
	svdir = *argv++;
	if (argv && *argv) {
		rplog = *argv;
		if (setup_log() != 1) {
			rplog = 0;
			warnx("log service disabled");
		}
	}
	curdir = open_read(".");
	if (curdir == -1)
		fatal2_cannot("open current directory", "");
	coe(curdir);

	stampcheck = monotonic_sec();

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

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

			if (stat(svdir, &s) != -1) {
				if (check || 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;
						check = 0;
						//if (now <= mtime)
						//	sleep(1);
						runsvdir();
						while (fchdir(curdir) == -1) {
							warn2_cannot("change directory, pausing", "");
							sleep(5);
						}
					} else
						warn2_cannot("change directory to ", svdir);
				}
			} else
				warn2_cannot("stat ", svdir);
		}

		if (rplog) {
			if ((int)(now - stamplog) >= 0) {
				write(logpipe[1], ".", 1);
				stamplog = now + 900;
			}
		}

		pfd[0].revents = 0;
		sig_block(SIGCHLD);
		deadline = (check ? 1 : 5);
		if (rplog)
			poll(pfd, 1, deadline*1000);
		else
			sleep(deadline);
		sig_unblock(SIGCHLD);

		if (pfd[0].revents & POLLIN) {
			while (read(logpipe[0], &ch, 1) > 0) {
				if (ch) {
					for (i = 6; i < rploglen; i++)
						rplog[i-1] = rplog[i];
					rplog[rploglen-1] = ch;
				}
			}
		}

		switch (exitsoon) {
		case 1:
			_exit(0);
		case 2:
			for (i = 0; i < svnum; i++)
				if (sv[i].pid)
					kill(sv[i].pid, SIGTERM);
			_exit(111);
		}
	}
	/* not reached */
	return 0;
}
Ejemplo n.º 5
0
static void runsvdir(void)
{
	DIR *dir;
	direntry *d;
	int i;
	struct stat s;

	dir = opendir(".");
	if (!dir) {
		warn2_cannot("open directory ", svdir);
		return;
	}
	for (i = 0; i < svnum; i++)
		sv[i].isgone = 1;
	errno = 0;
	while ((d = readdir(dir))) {
		if (d->d_name[0] == '.')
			continue;
		if (stat(d->d_name, &s) == -1) {
			warn2_cannot("stat ", d->d_name);
			errno = 0;
			continue;
		}
		if (!S_ISDIR(s.st_mode))
			continue;
		for (i = 0; i < svnum; i++) {
			if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
				sv[i].isgone = 0;
				if (!sv[i].pid)
					runsv(i, d->d_name);
				break;
			}
		}
		if (i == svnum) {
			/* new service */
			struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
			if (!svnew) {
				warn3x("cannot start runsv ", d->d_name,
						" too many services");
				continue;
			}
			sv = svnew;
			svnum++;
			memset(&sv[i], 0, sizeof(sv[i]));
			sv[i].ino = s.st_ino;
			sv[i].dev = s.st_dev;
			/*sv[i].pid = 0;*/
			/*sv[i].isgone = 0;*/
			runsv(i, d->d_name);
			check = 1;
		}
	}
	if (errno) {
		warn2_cannot("read directory ", svdir);
		closedir(dir);
		check = 1;
		return;
	}
	closedir(dir);

	/* SIGTERM removed runsv's */
	for (i = 0; i < svnum; i++) {
		if (!sv[i].isgone)
			continue;
		if (sv[i].pid)
			kill(sv[i].pid, SIGTERM);
		sv[i] = sv[--svnum];
		check = 1;
	}
}
Ejemplo n.º 6
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 (;;) */
}
Ejemplo n.º 7
0
/* gcc 4.3.0 does better with NOINLINE */
static NOINLINE int do_rescan(void)
{
	DIR *dir;
	struct dirent *d;
	int i;
	struct stat s;
	int need_rescan = 0;

	dir = opendir(".");
	if (!dir) {
		warn2_cannot("open directory ", svdir);
		return 1; /* need to rescan again soon */
	}
	for (i = 0; i < svnum; i++)
		sv[i].isgone = 1;

	while (1) {
		errno = 0;
		d = readdir(dir);
		if (!d)
			break;
		if (d->d_name[0] == '.')
			continue;
		if (stat(d->d_name, &s) == -1) {
			warn2_cannot("stat ", d->d_name);
			continue;
		}
		if (!S_ISDIR(s.st_mode))
			continue;
		/* Do we have this service listed already? */
		for (i = 0; i < svnum; i++) {
			if ((sv[i].ino == s.st_ino)
#if CHECK_DEVNO_TOO
			 && (sv[i].dev == s.st_dev)
#endif
			) {
				if (sv[i].pid == 0) /* restart if it has died */
					goto run_ith_sv;
				sv[i].isgone = 0; /* "we still see you" */
				goto next_dentry;
			}
		}
		{ /* Not found, make new service */
			struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
			if (!svnew) {
				warn2_cannot("start runsv ", d->d_name);
				need_rescan = 1;
				continue;
			}
			sv = svnew;
			svnum++;
#if CHECK_DEVNO_TOO
			sv[i].dev = s.st_dev;
#endif
			sv[i].ino = s.st_ino;
 run_ith_sv:
			sv[i].pid = runsv(d->d_name);
			sv[i].isgone = 0;
		}
 next_dentry: ;
	}
	i = errno;
	closedir(dir);
	if (i) { /* readdir failed */
		warn2_cannot("read directory ", svdir);
		return 1; /* need to rescan again soon */
	}

	/* Send SIGTERM to runsv whose directories
	 * were no longer found (-> must have been removed) */
	for (i = 0; i < svnum; i++) {
		if (!sv[i].isgone)
			continue;
		if (sv[i].pid)
			kill(sv[i].pid, SIGTERM);
		svnum--;
		sv[i] = sv[svnum];
		i--; /* so that we don't skip new sv[i] (bug was here!) */
	}
	return need_rescan;
}
Ejemplo n.º 8
0
int runsvdir_main(int argc, char **argv)
{
	struct stat s;
	time_t mtime = 0;
	int wstat;
	int curdir;
	int pid;
	struct taia deadline;
	struct taia now;
	struct taia stampcheck;
	char ch;
	int i;

	argv++;
	if (!argv || !*argv) usage();
	if (**argv == '-') {
		switch (*(*argv + 1)) {
		case 'P': pgrp = 1;
		case '-': ++argv;
		}
		if (!argv || !*argv) usage();
	}

	sig_catch(SIGTERM, s_term);
	sig_catch(SIGHUP, s_hangup);
	svdir = *argv++;
	if (argv && *argv) {
		rplog = *argv;
		if (setup_log() != 1) {
			rplog = 0;
			warnx("log service disabled");
		}
	}
	curdir = open_read(".");
	if (curdir == -1)
		fatal2_cannot("open current directory", "");
	coe(curdir);

	taia_now(&stampcheck);

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

		taia_now(&now);
		if (now.sec.x < (stampcheck.sec.x - 3)) {
			/* time warp */
			warnx("time warp: resetting time stamp");
			taia_now(&stampcheck);
			taia_now(&now);
			if (rplog) taia_now(&stamplog);
		}
		if (taia_less(&now, &stampcheck) == 0) {
			/* wait at least a second */
			taia_uint(&deadline, 1);
			taia_add(&stampcheck, &now, &deadline);

			if (stat(svdir, &s) != -1) {
				if (check || s.st_mtime != mtime
				 || s.st_ino != ino || s.st_dev != dev
				) {
					/* svdir modified */
					if (chdir(svdir) != -1) {
						mtime = s.st_mtime;
						dev = s.st_dev;
						ino = s.st_ino;
						check = 0;
						if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime))
							sleep(1);
						runsvdir();
						while (fchdir(curdir) == -1) {
							warn2_cannot("change directory, pausing", "");
							sleep(5);
						}
					} else
						warn2_cannot("change directory to ", svdir);
				}
			} else
				warn2_cannot("stat ", svdir);
		}

		if (rplog) {
			if (taia_less(&now, &stamplog) == 0) {
				write(logpipe[1], ".", 1);
				taia_uint(&deadline, 900);
				taia_add(&stamplog, &now, &deadline);
			}
		}
		taia_uint(&deadline, check ? 1 : 5);
		taia_add(&deadline, &now, &deadline);

		sig_block(SIGCHLD);
		if (rplog)
			iopause(io, 1, &deadline, &now);
		else
			iopause(0, 0, &deadline, &now);
		sig_unblock(SIGCHLD);

		if (rplog && (io[0].revents | IOPAUSE_READ))
			while (read(logpipe[0], &ch, 1) > 0)
				if (ch) {
					for (i = 6; i < rploglen; i++)
						rplog[i-1] = rplog[i];
					rplog[rploglen-1] = ch;
				}

		switch (exitsoon) {
		case 1:
			_exit(0);
		case 2:
			for (i = 0; i < svnum; i++)
				if (sv[i].pid)
					kill(sv[i].pid, SIGTERM);
			_exit(111);
		}
	}
	/* not reached */
	return 0;
}