Exemplo n.º 1
0
/**
 * service_bootstrap - Start bootstrap services and tasks
 *
 * System startup, runlevel S, where only services, tasks and
 * run commands absolutely essential to bootstrap are located.
 */
void service_bootstrap(void)
{
	svc_t *svc;

	_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
	for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
		svc_cmd_t cmd;

		/* Inetd services cannot be part of bootstrap currently. */
		if (svc_is_inetd(svc))
			continue;

		cmd = service_enabled(svc, 0, NULL);
		if (SVC_START == cmd  || (SVC_RELOAD == cmd))
			service_start(svc);
	}
}
Exemplo n.º 2
0
static int do_query_inetd(char *buf, size_t len)
{
	int id = 1;
	char *ptr, *input = sanitize(buf, len);
	svc_t *svc;

	if (!input)
		return -1;

	ptr = strchr(input, ':');
	if (ptr) {
		*ptr++ = 0;
		id = atonum(ptr);
	}

	svc = svc_find_by_jobid(atonum(input), id);
	if (!svc || !svc_is_inetd(svc)) {
		_e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", input);
		return 1;
	}

	return inetd_filter_str(&svc->inetd, buf, len);
}
Exemplo n.º 3
0
/* In verbose mode we skip the header and each service description.
 * This in favor of having all info on one line so a machine can more
 * easily parse it. */
static int show_status(char *UNUSED(arg))
{
	svc_t *svc;

	/* Fetch UTMP runlevel, needed for svc_status() call below */
	runlevel = runlevel_get();

	if (!verbose) {
		printf("#      Status   PID     Runlevels  Service               Description\n");
		printf("====================================================================================\n");
	}

	for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
		int  inetd = svc_is_inetd(svc);
		char jobid[10], args[512] = "";
		struct init_request rq = {
			.magic = INIT_MAGIC,
			.cmd = INIT_CMD_QUERY_INETD,
		};

		if (svc_is_unique(svc))
			snprintf(jobid, sizeof(jobid), "%d", svc->job);
		else
			snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);

		printf("%-5s  %7s  %-6d  %-9s  ", jobid, svc_status(svc), svc->pid, runlevel_string(svc->runlevels));
		if (!verbose) {
			printf("%-20s  %s\n", svc->cmd, svc->desc);
			continue;
		}

		if (inetd) {
			char *info;

			snprintf(rq.data, sizeof(rq.data), "%s", jobid);
			if (do_send(&rq, sizeof(rq))) {
				snprintf(args, sizeof(args), "Unknown inetd");
				info = args;
			} else {
				info = rq.data;
				if (strcmp("internal", svc->cmd)) {
					char *ptr = strchr(info, ' ');
					if (ptr)
						info = ptr + 1;
				}
			}

			printf("%s %s\n", svc->cmd, info);
		} else {
			int i;

			for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
				strlcat(args, svc->args[i], sizeof(args));
				strlcat(args, " ", sizeof(args));
			}

			printf("%s %s\n", svc->cmd, args);
		}
	}

	return 0;
}
Exemplo n.º 4
0
/**
 * service_register - Register service, task or run commands
 * @type:     %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
 * @line:     A complete command line with -- separated description text
 * @mtime:    The modification time if service is loaded from /etc/finit.d
 * @username: Optional username to run service as, or %NULL to run as root
 *
 * This function is used to register commands to be run on different
 * system runlevels with optional username.  The @type argument details
 * if it's service to bo monitored/respawned (daemon), a one-shot task
 * or a command that must run in sequence and not in parallell, like
 * service and task commands do.
 *
 * The @line can optionally start with a username, denoted by an @
 * character. Like this:
 *
 *     service @username [!0-6,S] <!EV> /path/to/daemon arg -- Description
 *     task @username [!0-6,S] /path/to/task arg            -- Description
 *     run  @username [!0-6,S] /path/to/cmd arg             -- Description
 *     inetd tcp/ssh nowait [2345] @root:root /sbin/sshd -i -- Description
 *
 * If the username is left out the command is started as root.  The []
 * brackets denote the allowed runlevels, if left out the default for a
 * service is set to [2-5].  Allowed runlevels mimic that of SysV init
 * with the addition of the 'S' runlevel, which is only run once at
 * startup.  It can be seen as the system bootstrap.  If a task or run
 * command is listed in more than the [S] runlevel they will be called
 * when changing runlevel.
 *
 * Services (daemons, not inetd services) also support an optional <!EV>
 * argument.  This is for services that, e.g., require a system gateway
 * or interface to be up before they are started.  Or restarted, or even
 * SIGHUP'ed, when the gateway changes or interfaces come and go.  The
 * special case when a service is declared with <!> means it does not
 * support SIGHUP but must be STOP/START'ed at system reconfiguration.
 *
 * Supported service events are: GW, IFUP[:ifname], IFDN[:ifname], where
 * the interface name (:ifname) is optional.  Actully, the check with a
 * service event declaration is string based, so 'IFUP:ppp' will match
 * any of "IFUP:ppp0" or "IFUP:pppoe1" sent by the netlink.so plugin.
 *
 * For multiple instances of the same command, e.g. multiple DHCP
 * clients, the user must enter an ID, using the :ID syntax.
 *
 *     service :1 /sbin/udhcpc -i eth1
 *     service :2 /sbin/udhcpc -i eth2
 *
 * Without the :ID syntax Finit will overwrite the first service line
 * with the contents of the second.  The :ID must be [1,MAXINT].
 *
 * Returns:
 * POSIX OK(0) on success, or non-zero errno exit status on failure.
 */
int service_register(int type, char *line, time_t mtime, char *username)
{
	int i = 0;
	int id = 1;		/* Default to ID:1 */
#ifndef INETD_DISABLED
	int forking = 0;
#endif
	char *service = NULL, *proto = NULL, *ifaces = NULL;
	char *cmd, *desc, *runlevels = NULL, *events = NULL;
	svc_t *svc;
	plugin_t *plugin = NULL;

	if (!line) {
		_e("Invalid input argument.");
		return errno = EINVAL;
	}

	desc = strstr(line, "-- ");
	if (desc)
		*desc = 0;

	cmd = strtok(line, " ");
	if (!cmd) {
	incomplete:
		_e("Incomplete service, cannot register.");
		return errno = ENOENT;
	}

	while (cmd) {
		if (cmd[0] != '/' && strchr(cmd, '/'))
			service = cmd;   /* inetd service/proto */
#ifndef INETD_DISABLED
		else if (!strncasecmp(cmd, "nowait", 6))
			forking = 1;
		else if (!strncasecmp(cmd, "wait", 4))
			forking = 0;
#endif
		else if (cmd[0] == '@')	/* @username[:group] */
			username = &cmd[1];
		else if (cmd[0] == '[')	/* [runlevels] */
			runlevels = &cmd[0];
		else if (cmd[0] == '<')	/* [!ev] */
			events = &cmd[1];
		else if (cmd[0] == ':')	/* :ID */
			id = atoi(&cmd[1]);
		else
			break;

		/* Check if valid command follows... */
		cmd = strtok(NULL, " ");
		if (!cmd)
			goto incomplete;
	}

	/* Example: inetd ssh/tcp@eth0,eth1 or 222/tcp@eth2 */
	if (service) {
		ifaces = strchr(service, '@');
		if (ifaces)
			*ifaces++ = 0;

		proto = strchr(service, '/');
		if (!proto)
			goto incomplete;
		*proto++ = 0;
	}

#ifndef INETD_DISABLED
	/* Find plugin that provides a callback for this inetd service */
	if (type == SVC_TYPE_INETD) {
		if (!strncasecmp(cmd, "internal", 8)) {
			char *ptr, *ps = service;

			/* internal.service */
			ptr = strchr(cmd, '.');
			if (ptr) {
				*ptr++ = 0;
				ps = ptr;
			}

			plugin = plugin_find(ps);
			if (!plugin || !plugin->inetd.cmd) {
				_e("Inetd service %s has no internal plugin, skipping.", service);
				return errno = ENOENT;
			}
		}

		/* Check if known inetd, then add ifnames for filtering only. */
		svc = find_inetd_svc(cmd, service, proto);
		if (svc)
			goto inetd_setup;

		id = svc_next_id(cmd);
	}
#endif

	svc = svc_find(cmd, id);
	if (!svc) {
		_d("Creating new svc for %s id #%d type %d", cmd, id, type);
		svc = svc_new(cmd, id, type);
		if (!svc) {
			_e("Out of memory, cannot register service %s", cmd);
			return errno = ENOMEM;
		}
	}

	/* New, recently modified or unchanged ... used on reload. */
	svc_check_dirty(svc, mtime);

	if (desc)
		strlcpy(svc->desc, desc + 3, sizeof(svc->desc));

	if (username) {
		char *ptr = strchr(username, ':');

		if (ptr) {
			*ptr++ = 0;
			strlcpy(svc->group, ptr, sizeof(svc->group));
		}
		strlcpy(svc->username, username, sizeof(svc->username));
	}

	if (plugin) {
		/* Internal plugin provides this service */
		svc->inetd.cmd = plugin->inetd.cmd;
	} else {
		strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
		while ((cmd = strtok(NULL, " ")))
			strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
		svc->args[i][0] = 0;

		plugin = plugin_find(svc->cmd);
		if (plugin && plugin->svc.cb) {
			svc->cb           = plugin->svc.cb;
			svc->dynamic      = plugin->svc.dynamic;
			svc->dynamic_stop = plugin->svc.dynamic_stop;
		}
	}

	svc->runlevels = conf_parse_runlevels(runlevels);
	_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);

	if (type == SVC_TYPE_SERVICE)
		conf_parse_events(svc, events);

#ifndef INETD_DISABLED
	if (svc_is_inetd(svc)) {
		char *iface, *name = service;

		if (svc->inetd.cmd && plugin)
			name = plugin->name;

		if (inetd_new(&svc->inetd, name, service, proto, forking, svc)) {
			_e("Failed registering new inetd service %s.", service);
			inetd_del(&svc->inetd);
			return svc_del(svc);
		}

	inetd_setup:
		if (!ifaces) {
			_d("No specific iface listed for %s, allowing ANY.", service);
			return inetd_allow(&svc->inetd, NULL);
		}

		for (iface = strtok(ifaces, ","); iface; iface = strtok(NULL, ",")) {
			if (iface[0] == '!')
				inetd_deny(&svc->inetd, &iface[1]);
			else
				inetd_allow(&svc->inetd, iface);
		}
	}
#endif

	return 0;
}
Exemplo n.º 5
0
/**
 * service_runlevel - Change to a new runlevel
 * @newlevel: New runlevel to activate
 *
 * Stops all services not in @newlevel and starts, or lets continue to run,
 * those in @newlevel.  Also updates @prevlevel and active @runlevel.
 */
void service_runlevel(int newlevel)
{
	svc_t *svc;

	if (runlevel == newlevel)
		return;

	if (newlevel < 0 || newlevel > 9)
		return;

	prevlevel = runlevel;
	runlevel  = newlevel;

	_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
	runlevel_set(prevlevel, newlevel);

	/* Make sure to (re)load all *.conf in /etc/finit.d/ */
	conf_reload_dynamic();

	_d("Stopping services services not allowed in new runlevel ...");
	for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
		if (!svc_in_runlevel(svc, runlevel)) {
#ifndef INETD_DISABLED
			if (svc_is_inetd(svc))
				inetd_stop(&svc->inetd);
			else
#endif
				service_stop(svc);
		}

		/* ... or disabled/removed services from /etc/finit.d/ */
		if (svc_is_dynamic(svc) && svc_is_changed(svc))
			service_stop(svc);
	}

	/* Prev runlevel services stopped, call hooks before starting new runlevel ... */
	_d("All services have been stoppped, calling runlevel change hooks ...");
	plugin_run_hooks(HOOK_RUNLEVEL_CHANGE);  /* Reconfigure HW/VLANs/etc here */

	_d("Starting services services new to this runlevel ...");
	for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
#ifndef INETD_DISABLED
		/* Inetd services have slightly different semantics */
		if (svc_is_inetd(svc)) {
			if (svc_in_runlevel(svc, runlevel))
				inetd_start(&svc->inetd);

			continue;
		}
#endif

		/* All other services consult their callback here */
		svc_dance(svc);
	}

	/* Cleanup stale services */
	svc_clean_dynamic(service_unregister);

	if (0 == runlevel) {
		do_shutdown(SIGUSR2);
		return;
	}
	if (6 == runlevel) {
		do_shutdown(SIGUSR1);
		return;
	}

	if (runlevel == 1)
		touch("/etc/nologin");	/* Disable login in single-user mode */
	else
		erase("/etc/nologin");

	if (0 != prevlevel)
		tty_runlevel(runlevel);
}
Exemplo n.º 6
0
/* Remember: service_enabled() must be called before calling service_start() */
int service_start(svc_t *svc)
{
	int respawn, sd = 0;
	pid_t pid;
	sigset_t nmask, omask;

	if (!svc)
		return 1;
	respawn = svc->pid != 0;

	/* Don't try and start service if it doesn't exist. */
	if (!fexist(svc->cmd) && !svc->inetd.cmd) {
		if (verbose) {
			char msg[80];

			snprintf(msg, sizeof(msg), "Service %s does not exist!", svc->cmd);
			print_desc("", msg);
			print_result(1);
		}

		return 1;
	}

	/* Ignore if finit is SIGSTOP'ed */
	if (is_norespawn())
		return 0;

#ifndef INETD_DISABLED
	if (svc_is_inetd(svc)) {
		char ifname[IF_NAMESIZE] = "UNKNOWN";

		sd = svc->inetd.watcher.fd;

		if (svc->inetd.type == SOCK_STREAM) {
			/* Open new client socket from server socket */
			sd = accept(sd, NULL, NULL);
			if (sd < 0) {
				FLOG_PERROR("Failed accepting inetd service %d/tcp", svc->inetd.port);
				return 1;
			}

			_d("New client socket %d accepted for inetd service %d/tcp", sd, svc->inetd.port);

			/* Find ifname by means of getsockname() and getifaddrs() */
			inetd_stream_peek(sd, ifname);
		} else {           /* SOCK_DGRAM */
			/* Find ifname by means of IP_PKTINFO sockopt --> ifindex + if_indextoname() */
			inetd_dgram_peek(sd, ifname);
		}

		if (!inetd_is_allowed(&svc->inetd, ifname)) {
			FLOG_INFO("Service %s on port %d not allowed from interface %s.",
				  svc->inetd.name, svc->inetd.port, ifname);
			if (svc->inetd.type == SOCK_STREAM)
				close(sd);

			return 1;
		}

		FLOG_INFO("Starting inetd service %s for requst from iface %s ...", svc->inetd.name, ifname);
	} else
#endif
	if (verbose) {
		if (svc_is_daemon(svc))
			print_desc("", svc->desc);
		else if (!respawn)
			print_desc("Starting ", svc->desc);
	}

	/* Block sigchild while forking.  */
	sigemptyset(&nmask);
	sigaddset(&nmask, SIGCHLD);
	sigprocmask(SIG_BLOCK, &nmask, &omask);

	pid = fork();
	sigprocmask(SIG_SETMASK, &omask, NULL);
	if (pid == 0) {
		int i = 0;
		int status;
#ifdef ENABLE_STATIC
		int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */
#else
		int uid = getuser(svc->username);
#endif
		struct sigaction sa;
		char *args[MAX_NUM_SVC_ARGS];

		sigemptyset(&nmask);
		sigaddset(&nmask, SIGCHLD);
		sigprocmask(SIG_UNBLOCK, &nmask, NULL);

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

		/* Set desired user */
		if (uid >= 0) {
			setuid(uid);

			/* Set default path for regular users */
			if (uid > 0)
				setenv("PATH", _PATH_DEFPATH, 1);
		}

		/* Serve copy of args to process in case it modifies them. */
		for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++)
			args[i] = svc->args[i];
		args[i] = NULL;

		/* Redirect inetd socket to stdin for service */
		if (svc_is_inetd(svc)) {
			/* sd set previously */
			dup2(sd, STDIN_FILENO);
			close(sd);
			dup2(STDIN_FILENO, STDOUT_FILENO);
			dup2(STDIN_FILENO, STDERR_FILENO);
		} else if (debug) {
			int fd;
			char buf[CMD_SIZE] = "";

			fd = open(CONSOLE, O_WRONLY | O_APPEND);
			if (-1 != fd) {
				dup2(fd, STDOUT_FILENO);
				dup2(fd, STDERR_FILENO);
				close(fd);
			}

			for (i = 0; i < MAX_NUM_SVC_ARGS && args[i]; i++) {
				char arg[MAX_ARG_LEN];

				snprintf(arg, sizeof(arg), "%s ", args[i]);
				if (strlen(arg) < (sizeof(buf) - strlen(buf)))
					strcat(buf, arg);
			}
			_e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf);
		}

		if (svc->inetd.cmd)
			status = svc->inetd.cmd(svc->inetd.type);
		else
			status = execv(svc->cmd, args); /* XXX: Maybe use execve() to be able to launch scripts? */

		if (svc_is_inetd(svc)) {
			if (svc->inetd.type == SOCK_STREAM) {
				close(STDIN_FILENO);
				close(STDOUT_FILENO);
				close(STDERR_FILENO);
			}
		}

		exit(status);
	}
	svc->pid = pid;

	if (svc_is_inetd(svc)) {
		if (svc->inetd.type == SOCK_STREAM)
			close(sd);
	} else {
		int result;

		if (SVC_TYPE_RUN == svc->type)
			result = WEXITSTATUS(complete(svc->cmd, pid));
		else if (!respawn)
			result = svc->pid > 1 ? 0 : 1;
		else
			result = 0;

		if (verbose)
			print_result(result);
	}

	return 0;
}