static void
mgt_reap_child(void)
{
	int i;
	int status = 0xffff;
	struct vsb *vsb;
	pid_t r = 0;

	assert(child_pid != -1);

	/*
	 * Close the CLI connections
	 * This signals orderly shut down to child
	 */
	mgt_cli_stop_child();
	if (child_cli_out >= 0)
		closex(&child_cli_out);
	if (child_cli_in >= 0)
		closex(&child_cli_in);

	/* Stop the poker */
	if (ev_poker != NULL) {
		vev_del(mgt_evb, ev_poker);
		free(ev_poker);
	}
	ev_poker = NULL;

	/* Stop the listener */
	if (ev_listen != NULL) {
		vev_del(mgt_evb, ev_listen);
		free(ev_listen);
		ev_listen = NULL;
	}

	/* Compose obituary */
	vsb = VSB_new_auto();
	XXXAN(vsb);

	/* Wait for child to die */
	for (i = 0; i < mgt_param.cli_timeout; i++) {
		r = waitpid(child_pid, &status, WNOHANG);
		if (r == child_pid)
			break;
		(void)sleep(1);
	}
	if (r == 0) {
		VSB_printf(vsb, "Child (%jd) not dying, killing", (intmax_t)r);

		/* Kick it Jim... */
		if (MGT_FEATURE(FEATURE_NO_COREDUMP))
			(void)kill(child_pid, SIGKILL);
		else
			(void)kill(child_pid, SIGQUIT);
		r = waitpid(child_pid, &status, 0);
	}
	if (r != child_pid)
		fprintf(stderr, "WAIT 0x%jx\n", (uintmax_t)r);
	assert(r == child_pid);

	MAC_reopen_sockets(NULL);

	VSB_printf(vsb, "Child (%jd) %s", (intmax_t)r,
	    status ? "died" : "ended");
	if (WIFEXITED(status) && WEXITSTATUS(status)) {
		VSB_printf(vsb, " status=%d", WEXITSTATUS(status));
		exit_status |= 0x20;
		if (WEXITSTATUS(status) == 1)
			VSC_C_mgt->child_exit = ++static_VSC_C_mgt.child_exit;
		else
			VSC_C_mgt->child_stop = ++static_VSC_C_mgt.child_stop;
	}
	if (WIFSIGNALED(status)) {
		VSB_printf(vsb, " signal=%d", WTERMSIG(status));
		exit_status |= 0x40;
		VSC_C_mgt->child_died = ++static_VSC_C_mgt.child_died;
	}
#ifdef WCOREDUMP
	if (WCOREDUMP(status)) {
		VSB_printf(vsb, " (core dumped)");
		exit_status |= 0x80;
		VSC_C_mgt->child_dump = ++static_VSC_C_mgt.child_dump;
	}
#endif
	AZ(VSB_finish(vsb));
	MGT_complain(status ? C_ERR : C_INFO, "%s", VSB_data(vsb));
	VSB_delete(vsb);

	/* Dispose of shared memory but evacuate panic messages first */
	if (heritage.panic_str[0] != '\0') {
		mgt_panic_record(r);
		mgt_SHM_Destroy(1);
		VSC_C_mgt->child_panic = ++static_VSC_C_mgt.child_panic;
	} else {
		mgt_SHM_Destroy(MGT_DO_DEBUG(DBG_VSM_KEEP));
	}
	mgt_SHM_Create();
	mgt_SHM_Commit();

	if (child_state == CH_RUNNING)
		child_state = CH_DIED;

	/* Pick up any stuff lingering on stdout/stderr */
	(void)child_listener(NULL, EV_RD);
	closex(&child_output);
	VLU_Destroy(child_std_vlu);

	child_pid = -1;

	MGT_complain(C_DEBUG, "Child cleanup complete");

	if (child_state == CH_DIED && mgt_param.auto_restart)
		mgt_launch_child(NULL);
	else if (child_state == CH_DIED)
		child_state = CH_STOPPED;
	else if (child_state == CH_STOPPING)
		child_state = CH_STOPPED;
}
Пример #2
0
int _close(int fd)
{
        check_init("_close");
        return closex(fd, smbw_libc._close);
}
static void
mgt_launch_child(struct cli *cli)
{
	pid_t pid;
	unsigned u;
	char *p;
	struct vev *e;
	int i, j, k, cp[2];
	struct sigaction sa;

	if (child_state != CH_STOPPED && child_state != CH_DIED)
		return;

	if (!MAC_sockets_ready(cli)) {
		child_state = CH_STOPPED;
		if (cli != NULL) {
			VCLI_SetResult(cli, CLIS_CANT);
			return;
		}
		MGT_complain(C_ERR,
		    "Child start failed: could not open sockets");
		return;
	}

	child_state = CH_STARTING;

	/* Open pipe for mgr->child CLI */
	AZ(pipe(cp));
	heritage.cli_in = cp[0];
	mgt_child_inherit(heritage.cli_in, "cli_in");
	child_cli_out = cp[1];

	/* Open pipe for child->mgr CLI */
	AZ(pipe(cp));
	heritage.cli_out = cp[1];
	mgt_child_inherit(heritage.cli_out, "cli_out");
	child_cli_in = cp[0];

	/*
	 * Open pipe for child stdout/err
	 * NB: not inherited, because we dup2() it to stdout/stderr in child
	 */
	AZ(pipe(cp));
	heritage.std_fd = cp[1];
	child_output = cp[0];

	AN(heritage.vsm);
	mgt_SHM_Size_Adjust();
	AN(heritage.vsm);
	AN(heritage.param);
	if ((pid = fork()) < 0) {
		/* XXX */
		perror("Could not fork child");
		exit(1);
	}
	if (pid == 0) {

		/* Redirect stdin/out/err */
		AZ(close(STDIN_FILENO));
		assert(open("/dev/null", O_RDONLY) == STDIN_FILENO);
		assert(dup2(heritage.std_fd, STDOUT_FILENO) == STDOUT_FILENO);
		assert(dup2(heritage.std_fd, STDERR_FILENO) == STDERR_FILENO);

		/*
		 * Close all FDs the child shouldn't know about
		 *
		 * We cannot just close these filedescriptors, some random
		 * library routine might miss it later on and wantonly close
		 * a FD we use at that point in time. (See bug #1841).
		 * We close the FD and replace it with /dev/null instead,
		 * That prevents security leakage, and gives the library
		 * code a valid FD to close when it discovers the changed
		 * circumstances.
		 */
		closelog();

		for (i = STDERR_FILENO + 1; i < CLOSE_FD_UP_TO; i++) {
			if (vbit_test(fd_map, i))
				continue;
			if (close(i) == 0) {
				k = open("/dev/null", O_RDONLY);
				assert(k >= 0);
				j = dup2(k, i);
				assert(j == i);
				AZ(close(k));
			}
		}
#ifdef HAVE_SETPROCTITLE
		setproctitle("Varnish-Chld %s", heritage.name);
#endif

		if (mgt_param.sigsegv_handler) {
			memset(&sa, 0, sizeof sa);
			sa.sa_sigaction = child_sigsegv_handler;
			sa.sa_flags = SA_SIGINFO;
			(void)sigaction(SIGSEGV, &sa, NULL);
			(void)sigaction(SIGBUS, &sa, NULL);
			(void)sigaction(SIGABRT, &sa, NULL);
		}
		(void)signal(SIGINT, SIG_DFL);
		(void)signal(SIGTERM, SIG_DFL);

		VJ_subproc(JAIL_SUBPROC_WORKER);

		child_main();

		exit(0);
	}
	assert(pid > 1);
	MGT_complain(C_DEBUG, "Child (%jd) Started", (intmax_t)pid);
	VSC_C_mgt->child_start = ++static_VSC_C_mgt.child_start;

	/* Close stuff the child got */
	closex(&heritage.std_fd);

	mgt_child_inherit(heritage.cli_in, NULL);
	closex(&heritage.cli_in);

	mgt_child_inherit(heritage.cli_out, NULL);
	closex(&heritage.cli_out);

	child_std_vlu = VLU_New(NULL, child_line, 0);
	AN(child_std_vlu);

	AZ(ev_listen);
	e = vev_new();
	XXXAN(e);
	e->fd = child_output;
	e->fd_flags = EV_RD;
	e->name = "Child listener";
	e->callback = child_listener;
	AZ(vev_add(mgt_evb, e));
	ev_listen = e;
	AZ(ev_poker);
	if (mgt_param.ping_interval > 0) {
		e = vev_new();
		XXXAN(e);
		e->timeout = mgt_param.ping_interval;
		e->callback = child_poker;
		e->name = "child poker";
		AZ(vev_add(mgt_evb, e));
		ev_poker = e;
	}

	mgt_cli_start_child(child_cli_in, child_cli_out);
	child_pid = pid;
	if (mgt_push_vcls_and_start(cli, &u, &p)) {
		VCLI_SetResult(cli, u);
		MGT_complain(C_ERR, "Child (%jd) Pushing vcls failed:\n%s",
		    (intmax_t)child_pid, p);
		free(p);
		child_state = CH_RUNNING;
		mgt_stop_child();
	} else
		child_state = CH_RUNNING;
}