Ejemplo n.º 1
0
int api_init(uev_ctx_t *ctx)
{
	int sd;
	struct sockaddr_un sun = {
		.sun_family = AF_UNIX,
		.sun_path   = INIT_SOCKET,
	};

	sd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (-1 == sd) {
		_pe("Failed starting external API socket");
		return 1;
	}

	erase(INIT_SOCKET);
	if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun)))
		goto error;

	if (-1 == listen(sd, 10))
		goto error;

	return uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ);

error:
	_pe("Failed intializing API socket");
	close(sd);
	return 1;
}
Ejemplo n.º 2
0
/**
 * service_enabled - Should the service run?
 * @svc:   Pointer to &svc_t object
 * @event: Dynamic event, opaque flag passed to callback
 * @arg:   Event argument, used only by external service plugins.
 *
 * This method calls an associated service callback, if registered by a
 * plugin, and returns the &svc_cmd_t status. If no plugin is registered
 * the service is statically enabled in /etc/finit.conf and the result
 * will always be %SVC_START.
 *
 * Returns:
 * Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD.
 */
svc_cmd_t service_enabled(svc_t *svc, int event, void *arg)
{
	if (!svc) {
		errno = EINVAL;
		return SVC_STOP;
	}

	if (!svc_in_runlevel(svc, runlevel))
		return SVC_STOP;

	/*
	 * Event conditions for services are ignored during bootstrap.
	 */
	if (runlevel && !event_service_cond(svc->events))
		return SVC_STOP;

	/* Is there a service plugin registered? */
	if (svc->cb) {
		int   status;
		pid_t pid;

		/* Let callback run in separate process so it doesn't crash PID 1 */
		pid = fork();
		if (-1 == pid) {
			_pe("Failed in %s callback", svc->cmd);
			return SVC_STOP;
		}

		if (!pid) {
			status = svc->cb(svc, event, arg);
			exit(status);
		}

		if (waitpid(pid, &status, 0) == -1) {
			_pe("Failed reading status from %s callback", svc->cmd);
			return SVC_STOP;
		}

		/* Callback normally exits here. */
		if (WIFEXITED(status))
			return WEXITSTATUS(status);

		/* Check for SEGFAULT or other error ... */
		if (WIFSIGNALED(status) && WCOREDUMP(status))
			_e("Callback to %s crashed!\n", svc->cmd);
		else
			_e("Callback to %s did not exit normally!\n", svc->cmd);

		return SVC_STOP;
	}

	/* No service plugin, default to start, since listed in finit.conf */
	return SVC_START;
}
Ejemplo n.º 3
0
static int parse_conf_dynamic(char *file, time_t mtime)
{
	FILE *fp = fopen(file, "r");

	if (!fp) {
		_pe("Failed opening %s", file);
		return 1;
	}

	while (!feof(fp)) {
		char line[LINE_SIZE] = "";

		if (!fgets(line, sizeof(line), fp))
			continue;

		chomp(line);
		_d("dyn conf: %s", line);

		parse_dynamic(line, mtime);
	}

	fclose(fp);

	return 0;
}
Ejemplo n.º 4
0
int watchdog(char *progname)
{
	int pid;

	pid = fork();
	if (pid == 0) {
		int fd, ret;

		fd = init(progname, WDT_DEVNODE);
		if (fd == -1) {
			if (ENOENT != errno)
				_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
			_exit(1);
		}

		ret = loop(fd, WDT_TIMEOUT);
		while (!handover) {
			/* Waiting for SIGTERM ... */
			sleep(1);

			/* Set lowest possible timeout on SIGTERM */
			ioctl(fd, WDIOC_SETTIMEOUT, &shutdown);
		}
		close(fd);

		_exit(ret);
	}

	return pid;
}
Ejemplo n.º 5
0
Archivo: tty.c Proyecto: jonasj76/finit
static void setup(void)
{
	if (plugin.io.fd > 0)
		close(plugin.io.fd);

	plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
	if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0)
		_pe("Failed starting TTY watcher");
}
Ejemplo n.º 6
0
static void pidfile_init(void *arg)
{
	struct context *ctx = arg;

	ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN,
				    IN_CREATE | IN_ATTRIB | IN_DELETE);
	if (ctx->wd < 0) {
		_pe("inotify_add_watch()");
		close(ctx->fd);
		return;
	}

	_d("pidfile monitor active");
}
Ejemplo n.º 7
0
Archivo: kprobe.c Proyecto: GeassDB/ply
static int kprobe_attach_one(kprobe_t *kp, const char *func)
{
	struct perf_event_attr attr = {};
	int efd, i, id;

	id = kprobe_event_id(kp, func);
	if (id < 0)
		return id;

	attr.type = PERF_TYPE_TRACEPOINT;
	attr.sample_type = PERF_SAMPLE_RAW;
	attr.sample_period = 1;
	attr.wakeup_events = 1;
	attr.config = id;

	for (i = 0; i < /* sysconf(_SC_NPROCESSORS_ONLN) */ 1; i++) {
		efd = perf_event_open(&attr, -1/*pid*/, i/*cpu*/, -1/*group_fd*/, 0);
		if (efd < 0) {
			perror("perf_event_open");
			return -errno;
		}

		if (ioctl(efd, PERF_EVENT_IOC_ENABLE, 0)) {
			perror("perf enable");
			close(efd);
			return -errno;
		}

		if (!i && ioctl(efd, PERF_EVENT_IOC_SET_BPF, kp->bfd)) {
			_pe("perf-set-bpf: %s", func);
			close(efd);
			return -errno;
		}

		if (kp->efds.len == kp->efds.cap) {
			size_t sz = kp->efds.cap * sizeof(*kp->efds.fds);

			kp->efds.fds = realloc(kp->efds.fds, sz << 1);
			assert(kp->efds.fds);
			kp->efds.cap <<= 1;
		}

		kp->efds.fds[kp->efds.len++] = efd;
	}
	return 1;
}
Ejemplo n.º 8
0
/*
 * Check all filesystems in /etc/fstab with a fs_passno > 0
 */
static int fsck(int pass)
{
//	int save;
	struct fstab *fs;

	if (!setfsent()) {
		_pe("Failed opening fstab");
		return 1;
	}

//	if ((save = log_is_debug()))
//		log_debug();

	while ((fs = getfsent())) {
		char cmd[80];
		struct stat st;

		if (fs->fs_passno != pass)
			continue;

		errno = 0;
		if (stat(fs->fs_spec, &st) || !S_ISBLK(st.st_mode)) {
			if (!string_match(fs->fs_spec, "UUID=") && !string_match(fs->fs_spec, "LABEL=")) {
				_d("Cannot fsck %s, not a block device: %s", fs->fs_spec, strerror(errno));
				continue;
			}
		}

		if (fismnt(fs->fs_file)) {
			_d("Skipping fsck of %s, already mounted on %s.", fs->fs_spec, fs->fs_file);
			continue;
		}

		snprintf(cmd, sizeof(cmd), "fsck -a %s", fs->fs_spec);
		run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
	}

//	if (save)
//		log_debug();
	endfsent();

	return 0;
}
Ejemplo n.º 9
0
static void pidfile_callback(void *UNUSED(arg), int fd, int UNUSED(events))
{
	static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
	static char cond[MAX_ARG_LEN];

	struct inotify_event *ev;
	ssize_t sz, len;
	char *basename;
	svc_t *svc;

	sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
	if (sz <= 0) {
		_pe("invalid inotify event");
		return;
	}
	ev_buf[sz] = 0;

	for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
	     len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
	     /* ev = (void *)(ev + 1) + ev->len, sz -= sizeof(*ev) + ev->len) { */
		if (!ev->mask || !strstr(ev->name, ".pid"))
			continue;

		basename = strtok(ev->name, ".");
		if (!basename)
			continue;

		svc = svc_find_by_nameid(basename, 1);
		if (!svc)
			continue;

		/* TODO FIXME XXX WKZ check that pid is controlled by finit */
		
		_d("%s: match %s", basename, svc->cmd);
		snprintf(cond, sizeof(cond), "svc%s", svc->cmd);
		if (ev->mask & (IN_CREATE | IN_ATTRIB)) {
			svc_started(svc);
			cond_set(cond);
		} else if (ev->mask & IN_DELETE)
			cond_clear(cond);
	}
}
Ejemplo n.º 10
0
Archivo: kprobe.c Proyecto: GeassDB/ply
static int kprobe_event_id(kprobe_t *kp, const char *func)
{
	FILE *fp;
	char *ev_id, ev_str[16];

	fprintf(kp->ctrl, "%s %s\n", kp->type, func);
	fflush(kp->ctrl);

	asprintf(&ev_id, "/sys/kernel/debug/tracing/events/kprobes/%s_%s_0/id",
		 kp->type, func);
	fp = fopen(ev_id, "r");
	free(ev_id);
	if (!fp) {
		_pe("unable to create kprobe for \"%s\"", func);
		return -EIO;
	}

	fgets(ev_str, sizeof(ev_str), fp);
	fclose(fp);
	return strtol(ev_str, NULL, 0);
}
Ejemplo n.º 11
0
static void nl_callback(void *arg, int sd, int events)
{
	ssize_t len;
	static char buf[4096];
	struct nlmsghdr *nh;

	memset(buf, 0, sizeof(buf));
	len = recv(sd, buf, sizeof(buf), 0);
	if (len < 0) {
		if (errno != EINTR)	/* Signal */
			_pe("recv()");
		return;
	}

	for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
		//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
		if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
			nl_route(nh);
		else
			nl_link(nh);
	}
}
Ejemplo n.º 12
0
int main(int argc, char* argv[])
{
	char *path;
	char cmd[256];
	int udev = 0;
	uev_t timer;	       /* Bootstrap timer, on timeout call finalize() */
	uev_ctx_t loop;

	/*
	 * finit/init/telinit client tool uses /dev/initctl pipe
	 * for compatibility but initctl client tool uses socket
	 */
	if (getpid() != 1)
		return client(argc, argv);

	/*
	 * Initalize event context.
	 */
	uev_init(&loop);
	ctx = &loop;

	/*
	 * Set PATH and SHELL early to something sane
	 */
	setenv("PATH", _PATH_STDPATH, 1);
	setenv("SHELL", _PATH_BSHELL, 1);

	/*
	 * Mount base file system, kernel is assumed to run devtmpfs for /dev
	 */
	chdir("/");
	umask(0);
	mount("none", "/proc", "proc", 0, NULL);
	mount("none", "/sys", "sysfs", 0, NULL);
	if (fisdir("/proc/bus/usb"))
		mount("none", "/proc/bus/usb", "usbfs", 0, NULL);

	/*
	 * Parse kernel command line (debug, rescue, splash, etc.)
	 * Also calls log_init() to set correct log level
	 */
	conf_parse_cmdline();

	/* Set up canvas */
	if (!rescue && !log_is_debug())
		screen_init();

	/*
	 * In case of emergency.
	 */
	emergency_shell();

	/*
	 * Initial setup of signals, ignore all until we're up.
	 */
	sig_init();

	/*
	 * Load plugins early, finit.conf may contain references to
	 * features implemented by plugins.
	 */
	plugin_init(&loop);

	/*
	 * Hello world.
	 */
	banner();

	/*
	 * Check file filesystems in /etc/fstab
	 */
	for (int pass = 1; pass < 10 && !rescue; pass++) {
		if (fsck(pass))
			break;
	}

	/*
	 * Initialize .conf system and load static /etc/finit.conf
	 * Also initializes global_rlimit[] for udevd, below.
	 */
	conf_init();

	/*
	 * Some non-embedded systems without an initramfs may not have /dev mounted yet
	 * If they do, check if system has udevadm and perform cleanup from initramfs
	 */
	if (!fismnt("/dev"))
		mount("udev", "/dev", "devtmpfs", MS_RELATIME, "size=10%,nr_inodes=61156,mode=755");
	else if (whichp("udevadm"))
		run_interactive("udevadm info --cleanup-db", "Cleaning up udev db");

	/* Some systems use /dev/pts */
	makedir("/dev/pts", 0755);
	mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");

	/*
	 * Some systems rely on us to both create /dev/shm and, to mount
	 * a tmpfs there.  Any system with dbus needs shared memory, so
	 * mount it, unless its already mounted, but not if listed in
	 * the /etc/fstab file already.
	 */
	makedir("/dev/shm", 0755);
	if (!fismnt("/dev/shm") && !ismnt("/etc/fstab", "/dev/shm"))
		mount("shm", "/dev/shm", "tmpfs", 0, NULL);

	/*
	 * New tmpfs based /run for volatile runtime data
	 * For details, see http://lwn.net/Articles/436012/
	 */
	if (fisdir("/run") && !fismnt("/run"))
		mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%");
	umask(022);

	/* Bootstrap conditions, needed for hooks */
	cond_init();

	/*
	 * Populate /dev and prepare for runtime events from kernel.
	 * Prefer udev if mdev is also available on the system.
	 */
	path = which("udevd");
	if (!path)
		path = which("/lib/systemd/systemd-udevd");
	if (path) {
		/* Desktop and server distros usually have a variant of udev */
		udev = 1;

		/* Register udevd as a monitored service */
		snprintf(cmd, sizeof(cmd), "[S12345789] pid:udevd %s -- Device event managing daemon", path);
		if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
			_pe("Failed registering %s", path);
			udev = 0;
		} else {
			snprintf(cmd, sizeof(cmd), ":1 [S] <svc%s> "
				 "udevadm trigger -c add -t devices "
				 "-- Requesting device events", path);
			service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);

			snprintf(cmd, sizeof(cmd), ":2 [S] <svc%s> "
				 "udevadm trigger -c add -t subsystems "
				 "-- Requesting subsystem events", path);
			service_register(SVC_TYPE_RUN, cmd, global_rlimit, NULL);
		}
		free(path);
	} else {
		path = which("mdev");
		if (path) {
			/* Embedded Linux systems usually have BusyBox mdev */
			if (log_is_debug())
				touch("/dev/mdev.log");

			snprintf(cmd, sizeof(cmd), "%s -s", path);
			free(path);

			run_interactive(cmd, "Populating device tree");
		}
	}

	/*
	 * Start built-in watchdog as soon as possible, if enabled
	 */
	wdogpid = watchdog(argv[0]);

	/*
	 * Mount filesystems
	 */
	if (!rescue) {
#ifdef REMOUNT_ROOTFS
		run("mount -n -o remount,rw /");
#endif
#ifdef SYSROOT
		mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
#endif
	}

	if (!rescue) {
		_d("Root FS up, calling hooks ...");
		plugin_run_hooks(HOOK_ROOTFS_UP);

		umask(0);
		if (run_interactive("mount -na", "Mounting filesystems"))
			plugin_run_hooks(HOOK_MOUNT_ERROR);

		_d("Calling extra mount hook, after mount -a ...");
		plugin_run_hooks(HOOK_MOUNT_POST);

		run("swapon -ea");
		umask(0022);
	}

	/* Base FS up, enable standard SysV init signals */
	sig_setup(&loop);

	if (!rescue) {
		_d("Base FS up, calling hooks ...");
		plugin_run_hooks(HOOK_BASEFS_UP);
	}

	/*
	 * Set up inotify watcher for /etc/finit.d and read all .conf
	 * files to figure out how to bootstrap the system.
	 */
	conf_monitor(&loop);

	/*
	 * Initalize state machine and start all bootstrap tasks
	 * NOTE: no network available!
	 */
	sm_init(&sm);
	sm_step(&sm);

	/* Debian has this little script to copy generated rules while the system was read-only */
	if (udev && fexist("/lib/udev/udev-finish"))
		run_interactive("/lib/udev/udev-finish", "Finalizing udev");

	/* Start new initctl API responder */
	api_init(&loop);
	umask(022);

	/*
	 * Wait for all SVC_TYPE_RUNTASK to have completed their work in
	 * [S], or timeout, before calling finalize()
	 */
	_d("Starting bootstrap finalize timer ...");
	uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000);

	/*
	 * Enter main loop to monior /dev/initctl and services
	 */
	_d("Entering main loop ...");
	return uev_run(&loop, 0);
}
Ejemplo n.º 13
0
static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
{
	int sd;
	struct init_request rq;

	sd = accept(w->fd, NULL, NULL);
	if (sd < 0) {
		_pe("Failed serving API request");
		return;
	}

	while (1) {
		int result = 0;
		ssize_t len;

		len = read(sd, &rq, sizeof(rq));
		if (len <= 0) {
			if (-1 == len) {
				if (EINTR == errno)
					continue;

				if (EAGAIN == errno)
					break;

				_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
			}

			break;
		}

		if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
			_e("Invalid initctl request.");
			break;
		}

		switch (rq.cmd) {
		case INIT_CMD_RUNLVL:
			switch (rq.runlevel) {
			case '0':
				_d("Halting system (SIGUSR2)");
				do_shutdown(SIGUSR2);
				break;

			case 's':
			case 'S':
				_d("Cannot enter bootstrap after boot ...");
				rq.runlevel = '1';
				/* Fall through to regular processing */

			case '1'...'5':
			case '7'...'9':
				_d("Setting new runlevel %c", rq.runlevel);
				service_runlevel(rq.runlevel - '0');
				break;

			case '6':
				_d("Rebooting system (SIGUSR1)");
				do_shutdown(SIGUSR1);
				break;

			default:
				_d("Unsupported runlevel: %d", rq.runlevel);
				break;
			}
			break;

		case INIT_CMD_DEBUG:
			debug = !debug;
			if (debug)
				silent = 0;
			else
				silent = quiet ? 1 : SILENT_MODE;
			break;

		case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
			service_reload_dynamic();
			break;

		case INIT_CMD_START_SVC:
			result = do_start(rq.data, sizeof(rq.data));
			break;

		case INIT_CMD_STOP_SVC:
			result = do_pause(rq.data, sizeof(rq.data));
			break;

		case INIT_CMD_RESTART_SVC:
			result = do_restart(rq.data, sizeof(rq.data));
			break;

#ifndef INETD_DISABLED
		case INIT_CMD_QUERY_INETD:
			result = do_query_inetd(rq.data, sizeof(rq.data));
			break;
#endif

		case INIT_CMD_EMIT:
			result = do_handle_emit(rq.data, sizeof(rq.data));
			break;

		case INIT_CMD_GET_RUNLEVEL:
			rq.runlevel = runlevel;
			result = 0;
			break;

		case INIT_CMD_ACK:
			_d("Client failed reading ACK.");
			goto leave;

		default:
			_d("Unsupported cmd: %d", rq.cmd);
			break;
		}

		if (result)
			rq.cmd = INIT_CMD_NACK;
		else
			rq.cmd = INIT_CMD_ACK;
		len = write(sd, &rq, sizeof(rq));
		if (len != sizeof(rq))
			_d("Failed sending ACK/NACK back to client.");
	}

leave:
	close(sd);
}
Ejemplo n.º 14
0
int run(char *cmd)
{
	int status, result, i = 0;
	char *args[NUM_ARGS + 1], *arg, *backup;
	pid_t pid;

	/* We must create a copy that is possible to modify. */
	backup = arg = strdup(cmd);
	if (!arg)
		return 1; /* Failed allocating a string to be modified. */

	/* Split command line into tokens of an argv[] array. */
	args[i++] = strsep(&arg, "\t ");
	while (arg && i < NUM_ARGS) {
		/* Handle run("su -c \"dbus-daemon --system\" messagebus");
		 *   => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
		if (*arg == '\'' || *arg == '"') {
			char *p, delim[2] = " ";

			delim[0]  = arg[0];
			args[i++] = arg++;
			strsep(&arg, delim);
			 p     = arg - 1;
			*p     = *delim;
			*arg++ = 0;
		} else {
			args[i++] = strsep(&arg, "\t ");
		}
	}
	args[i] = NULL;
#if 0
	_e("Splitting: '%s' =>", cmd);
	for (i = 0; args[i]; i++)
		_e("\t%s", args[i]);
#endif
	if (i == NUM_ARGS && args[i]) {
		_e("Command too long: %s", cmd);
		free(backup);
		errno = EOVERFLOW;
		return 1;
	}

	pid = fork();
	if (0 == pid) {
		int i;
		FILE *fp;
		struct sigaction sa;

		/* Reset signal handlers that were set by the parent process */
		for (i = 1; i < NSIG; i++)
			DFLSIG(sa, i, 0);

		/* Always redirect stdio for run() */
		fp = fopen("/dev/null", "w");
		if (fp) {
			int fd = fileno(fp);

			dup2(fd, STDIN_FILENO);
			dup2(fd, STDOUT_FILENO);
			dup2(fd, STDERR_FILENO);
		}

		sig_unblock();
		execvp(args[0], args);

		_exit(1); /* Only if execv() fails. */
	} else if (-1 == pid) {
		_pe("%s", args[0]);
		free(backup);

		return -1;
	}

	status = complete(args[0], pid);
	if (-1 == status) {
		free(backup);
		return 1;
	}

	result = WEXITSTATUS(status);
	if (WIFEXITED(status)) {
		_d("Started %s and ended OK: %d", args[0], result);
	} else if (WIFSIGNALED(status)) {
		_d("Process %s terminated by signal %d", args[0], WTERMSIG(status));
		if (!result)
			result = 1; /* Must alert callee that the command did complete successfully.
				     * This is necessary since not all programs trap signals and
				     * change their return code accordingly. --Jocke */
	}

	free(backup);

	return result;
}
Ejemplo n.º 15
0
pid_t run_getty(char *cmd, char *args[], int console)
{
	pid_t pid = fork();

	if (!pid) {
		int fd;
		char c;

		if (console) {
			/* Detach from initial controlling TTY */
			vhangup();

			close(STDERR_FILENO);
			close(STDOUT_FILENO);
			close(STDIN_FILENO);

			/* Attach TTY to console */
			fd = open(CONSOLE, O_RDWR);
			if (fd != STDIN_FILENO)
				exit(1);

			dup2(0, STDIN_FILENO);
			dup2(0, STDOUT_FILENO);
			dup2(0, STDERR_FILENO);

			prctl(PR_SET_NAME, "console", 0, 0, 0);
		}

		sig_unblock();

		setsid();

		if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
			_pe("Failed TIOCSCTTY");

		while (!fexist(SYNC_SHUTDOWN)) {
			static const char msg[] = "\nPlease press Enter to activate this console.";

			if (fexist(SYNC_STOPPED)) {
				sleep(1);
				continue;
			}

			if (console) {
				(void)write(STDERR_FILENO, msg, sizeof(msg));
				while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
					continue;
			}
			if (fexist(SYNC_STOPPED))
				continue;

			execv(cmd, args);
		}

		if (console)
			close(fd);

		exit(0);
	}

	return pid;
}
Ejemplo n.º 16
0
Archivo: service.c Proyecto: wkz/finit
/**
 * service_enabled - Should the service run?
 * @svc:   Pointer to &svc_t object
 * @event: Dynamic event, opaque flag passed to callback
 * @arg:   Event argument, used only by external service plugins.
 *
 * This method calls an associated service callback, if registered by a
 * plugin, and returns the &svc_cmd_t status. If no plugin is registered
 * the service is statically enabled in /etc/finit.conf and the result
 * will always be %SVC_START.
 *
 * Returns:
 * Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD.
 */
svc_cmd_t service_enabled(svc_t *svc, int event, void *arg)
{
	svc_cmd_t cmd = SVC_START; /* Default to start, since listed in finit.conf */

	if (!svc) {
		errno = EINVAL;
		return SVC_STOP;
	}

	if (!svc_in_runlevel(svc, runlevel))
		return SVC_STOP;

	/*
	 * Event conditions for services are ignored during bootstrap.
	 */
	_d("Checking %s runlevel %d and events %s", svc->cmd, runlevel, svc->events);
	if (runlevel && !event_service_cond(svc->events))
		return SVC_STOP;

	if (svc->state == SVC_RELOAD_STATE)
		cmd = SVC_RELOAD;
	if (svc->state == SVC_PAUSED_STATE)
		cmd = SVC_STOP;

	/* Is there a service plugin registered? */
	if (svc->cb) {
		int   status;
		pid_t pid;

		/* Let callback run in separate process so it doesn't crash PID 1 */
		pid = fork();
		if (-1 == pid) {
			_pe("Failed in %s callback", svc->cmd);
			return SVC_STOP;
		}

		if (!pid)
			_exit(svc->cb(svc, event, arg));

		if (waitpid(pid, &status, 0) == -1) {
			_pe("Failed reading status from %s callback", svc->cmd);
			return SVC_STOP;
		}

		/* Callback normally exits here. */
		if (WIFEXITED(status)) {
			svc_cmd_t tmp = WEXITSTATUS(status);
			return tmp == SVC_START ? cmd : tmp;
		}

		/* Check for SEGFAULT or other error ... */
		if (WIFSIGNALED(status) && WCOREDUMP(status))
			_e("Callback to %s crashed!\n", svc->cmd);
		else
			_e("Callback to %s did not exit normally!\n", svc->cmd);

		return SVC_STOP;
	}

	_d("%s => %s", svc->cmd, (cmd == SVC_START
				  ? "SVC_START"
				  : (cmd == SVC_RELOAD
				     ? "SVC_RELOAD"
				     : "SVC_STOP")));

	return cmd;
}