Fix stacked runlevel support
Patch was provided by Max Hacking <max.gentoo.bugzilla@hacking.co.uk> and slightly fixed by Alexander Vershilov <qnikst@gentoo.org> and William Hubbs <williamh@gentoo.org>. Fixes: 1). Rebase to newest OpenRC version. 2). Remove code style fixes. Port to currect code style. 3). Fix rc_runlevel_stack instead of introducing new function. 4). Make get_runlevel_chain a private function. X-Gentoo-Bug: 467368 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=467368
This commit is contained in:
		
				
					committed by
					
						
						William Hubbs
					
				
			
			
				
	
			
			
			
						parent
						
							445b297360
						
					
				
				
					commit
					7716bf31de
				
			@@ -323,6 +323,42 @@ rc_parse_service_state(RC_SERVICE state)
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns a list of all the chained runlevels used by the
 | 
			
		||||
 * specified runlevel in dependency order, including the
 | 
			
		||||
 * specified runlevel. */
 | 
			
		||||
static void
 | 
			
		||||
get_runlevel_chain(const char *runlevel, RC_STRINGLIST *level_list)
 | 
			
		||||
{
 | 
			
		||||
	char path[PATH_MAX];
 | 
			
		||||
	RC_STRINGLIST *dirs;
 | 
			
		||||
	RC_STRING *d, *dn;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If we haven't been passed a runlevel or a level list, or
 | 
			
		||||
	 * if the passed runlevel doesn't exist then we're done already!
 | 
			
		||||
	 */
 | 
			
		||||
	if (!runlevel || !level_list || !rc_runlevel_exists(runlevel))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We want to add this runlevel to the list but if
 | 
			
		||||
	 * it is already in the list it needs to go at the
 | 
			
		||||
	 * end again.
 | 
			
		||||
	 */
 | 
			
		||||
	if (rc_stringlist_find(level_list, runlevel))
 | 
			
		||||
		rc_stringlist_delete(level_list, runlevel);
 | 
			
		||||
	rc_stringlist_add(level_list, runlevel);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We can now do exactly the above procedure for our chained
 | 
			
		||||
	 * runlevels.
 | 
			
		||||
	 */
 | 
			
		||||
	snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
 | 
			
		||||
	dirs = ls_dir(path, LS_DIR);
 | 
			
		||||
	TAILQ_FOREACH_SAFE(d, dirs, entries, dn)
 | 
			
		||||
		get_runlevel_chain(d->value, level_list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
rc_runlevel_starting(void)
 | 
			
		||||
{
 | 
			
		||||
@@ -424,22 +460,10 @@ librc_hidden_def(rc_runlevel_unstack)
 | 
			
		||||
RC_STRINGLIST *
 | 
			
		||||
rc_runlevel_stacks(const char *runlevel)
 | 
			
		||||
{
 | 
			
		||||
	char path[PATH_MAX];
 | 
			
		||||
	RC_STRINGLIST *dirs;
 | 
			
		||||
	RC_STRING *d, *dn;
 | 
			
		||||
 | 
			
		||||
	if (!runlevel)
 | 
			
		||||
		return false;
 | 
			
		||||
	snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
 | 
			
		||||
	dirs = ls_dir(path, LS_DIR);
 | 
			
		||||
	TAILQ_FOREACH_SAFE(d, dirs, entries, dn) {
 | 
			
		||||
		if (!rc_runlevel_exists(d->value)) {
 | 
			
		||||
			TAILQ_REMOVE(dirs, d, entries);
 | 
			
		||||
			free(d->value);
 | 
			
		||||
			free(d);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return dirs;
 | 
			
		||||
	RC_STRINGLIST *stack;
 | 
			
		||||
	stack = rc_stringlist_new();
 | 
			
		||||
	get_runlevel_chain(runlevel, stack);
 | 
			
		||||
	return stack;
 | 
			
		||||
}
 | 
			
		||||
librc_hidden_def(rc_runlevel_stacks)
 | 
			
		||||
 | 
			
		||||
@@ -903,17 +927,13 @@ rc_services_in_runlevel_stacked(const char *runlevel)
 | 
			
		||||
	stacks = rc_runlevel_stacks(runlevel);
 | 
			
		||||
	TAILQ_FOREACH(stack, stacks, entries) {
 | 
			
		||||
		sl = rc_services_in_runlevel(stack->value);
 | 
			
		||||
		if (list != NULL) {
 | 
			
		||||
			TAILQ_CONCAT(list, sl, entries);
 | 
			
		||||
			free(sl);
 | 
			
		||||
		} else
 | 
			
		||||
			list = sl;
 | 
			
		||||
		TAILQ_CONCAT(list, sl, entries);
 | 
			
		||||
		free(sl);
 | 
			
		||||
	}
 | 
			
		||||
	return list;
 | 
			
		||||
}
 | 
			
		||||
librc_hidden_def(rc_services_in_runlevel_stacked)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RC_STRINGLIST *
 | 
			
		||||
rc_services_in_state(RC_SERVICE state)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -171,6 +171,26 @@ print_services(const char *runlevel, RC_STRINGLIST *svcs)
 | 
			
		||||
	rc_stringlist_free(l);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
print_stacked_services(const char *runlevel)
 | 
			
		||||
{
 | 
			
		||||
	RC_STRINGLIST *stackedlevels, *servicelist;
 | 
			
		||||
	RC_STRING *stackedlevel;
 | 
			
		||||
 | 
			
		||||
	stackedlevels = rc_runlevel_stacks(runlevel);
 | 
			
		||||
	TAILQ_FOREACH(stackedlevel, stackedlevels, entries) {
 | 
			
		||||
		if (rc_stringlist_find(levels, stackedlevel->value) != NULL)
 | 
			
		||||
			continue;
 | 
			
		||||
		print_level("Stacked", stackedlevel->value);
 | 
			
		||||
		servicelist = rc_services_in_runlevel(stackedlevel->value);
 | 
			
		||||
		print_services(stackedlevel->value, servicelist);
 | 
			
		||||
		rc_stringlist_free(servicelist);
 | 
			
		||||
		print_stacked_services(stackedlevel->value);
 | 
			
		||||
	}
 | 
			
		||||
	rc_stringlist_free(stackedlevels);
 | 
			
		||||
	stackedlevels = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "_usage.h"
 | 
			
		||||
#define usagestring ""						\
 | 
			
		||||
	"Usage: rc-status [options] <runlevel>...\n"		\
 | 
			
		||||
@@ -199,7 +219,8 @@ static const char * const longopts_help[] = {
 | 
			
		||||
int
 | 
			
		||||
rc_status(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	RC_STRING *s, *l, *t;
 | 
			
		||||
    RC_STRING *s, *l, *t, *level;
 | 
			
		||||
 | 
			
		||||
	char *p, *runlevel = NULL;
 | 
			
		||||
	int opt, aflag = 0, retval = 0;
 | 
			
		||||
 | 
			
		||||
@@ -280,16 +301,7 @@ rc_status(int argc, char **argv)
 | 
			
		||||
		print_level(NULL, l->value);
 | 
			
		||||
		services = rc_services_in_runlevel(l->value);
 | 
			
		||||
		print_services(l->value, services);
 | 
			
		||||
		nservices = rc_runlevel_stacks(l->value);
 | 
			
		||||
		TAILQ_FOREACH(s, nservices, entries) {
 | 
			
		||||
			if (rc_stringlist_find(levels, s->value) != NULL)
 | 
			
		||||
				continue;
 | 
			
		||||
			print_level("Stacked", s->value);
 | 
			
		||||
			sservices = rc_services_in_runlevel(s->value);
 | 
			
		||||
			print_services(s->value, sservices);
 | 
			
		||||
			rc_stringlist_free(sservices);
 | 
			
		||||
		}
 | 
			
		||||
		sservices = NULL;
 | 
			
		||||
		print_stacked_services(l->value);
 | 
			
		||||
		rc_stringlist_free(nservices);
 | 
			
		||||
		nservices = NULL;
 | 
			
		||||
		rc_stringlist_free(services);
 | 
			
		||||
@@ -317,16 +329,14 @@ rc_status(int argc, char **argv)
 | 
			
		||||
		services = rc_services_in_runlevel(NULL);
 | 
			
		||||
		sservices = rc_stringlist_new();
 | 
			
		||||
		TAILQ_FOREACH(l, levels, entries) {
 | 
			
		||||
			nservices = rc_services_in_runlevel(l->value);
 | 
			
		||||
			nservices = rc_services_in_runlevel_stacked(l->value);
 | 
			
		||||
			TAILQ_CONCAT(sservices, nservices, entries);
 | 
			
		||||
			free(nservices);
 | 
			
		||||
		}
 | 
			
		||||
		TAILQ_FOREACH_SAFE(s, services, entries, t) {
 | 
			
		||||
			if (rc_stringlist_find(sservices, s->value) ||
 | 
			
		||||
			    rc_service_state(s->value) &
 | 
			
		||||
			    (RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED))
 | 
			
		||||
		{
 | 
			
		||||
			TAILQ_REMOVE(services, s, entries);
 | 
			
		||||
			if ((rc_stringlist_find(sservices, s->value) ||
 | 
			
		||||
			    (rc_service_state(s->value) & ( RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)))) {
 | 
			
		||||
				TAILQ_REMOVE(services, s, entries);
 | 
			
		||||
				free(s->value);
 | 
			
		||||
				free(s);
 | 
			
		||||
			}
 | 
			
		||||
@@ -337,18 +347,17 @@ rc_status(int argc, char **argv)
 | 
			
		||||
		alist = rc_stringlist_new();
 | 
			
		||||
		l = rc_stringlist_add(alist, "");
 | 
			
		||||
		p = l->value;
 | 
			
		||||
		if (!runlevel)
 | 
			
		||||
			runlevel = rc_runlevel_get();
 | 
			
		||||
		TAILQ_FOREACH_SAFE(s, services, entries, t) {
 | 
			
		||||
			l->value = s->value;
 | 
			
		||||
			unsetenv("RC_SVCNAME");
 | 
			
		||||
			setenv("RC_SVCNAME", l->value, 1);
 | 
			
		||||
			tmp = rc_deptree_depends(deptree, needsme, alist, runlevel, RC_DEP_TRACE);
 | 
			
		||||
			if (TAILQ_FIRST(tmp)) {
 | 
			
		||||
				TAILQ_REMOVE(services, s, entries);
 | 
			
		||||
				TAILQ_INSERT_TAIL(nservices, s, entries);
 | 
			
		||||
		TAILQ_FOREACH(level, levels, entries) {
 | 
			
		||||
			TAILQ_FOREACH_SAFE(s, services, entries, t) {
 | 
			
		||||
				l->value = s->value;
 | 
			
		||||
				setenv("RC_SVCNAME", l->value, 1);
 | 
			
		||||
				tmp = rc_deptree_depends(deptree, needsme, alist, level->value, RC_DEP_TRACE);
 | 
			
		||||
				if (TAILQ_FIRST(tmp)) {
 | 
			
		||||
					TAILQ_REMOVE(services, s, entries);
 | 
			
		||||
					TAILQ_INSERT_TAIL(nservices, s, entries);
 | 
			
		||||
				}
 | 
			
		||||
				rc_stringlist_free(tmp);
 | 
			
		||||
			}
 | 
			
		||||
			rc_stringlist_free(tmp);
 | 
			
		||||
		}
 | 
			
		||||
		l->value = p;
 | 
			
		||||
		/* we are unsetting RC_SVCNAME because last loaded service 
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										92
									
								
								src/rc/rc.c
									
									
									
									
									
								
							
							
						
						
									
										92
									
								
								src/rc/rc.c
									
									
									
									
									
								
							@@ -79,12 +79,6 @@ const char rc_copyright[] = "Copyright (c) 2007-2008 Roy Marples";
 | 
			
		||||
 | 
			
		||||
const char *applet = NULL;
 | 
			
		||||
static char *runlevel;
 | 
			
		||||
static RC_STRINGLIST *hotplugged_services;
 | 
			
		||||
static RC_STRINGLIST *stop_services;
 | 
			
		||||
static RC_STRINGLIST *start_services;
 | 
			
		||||
static RC_STRINGLIST *types_n;
 | 
			
		||||
static RC_STRINGLIST *types_nua;
 | 
			
		||||
static RC_DEPTREE *deptree;
 | 
			
		||||
static RC_HOOK hook_out;
 | 
			
		||||
 | 
			
		||||
struct termios *termios_orig = NULL;
 | 
			
		||||
@@ -524,7 +518,9 @@ runlevel_config(const char *service, const char *level)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
do_stop_services(const char *newlevel, bool parallel, bool going_down)
 | 
			
		||||
do_stop_services(const RC_STRINGLIST *types_n, const RC_STRINGLIST *start_services,
 | 
			
		||||
				 const RC_STRINGLIST *stop_services, const RC_DEPTREE *deptree,
 | 
			
		||||
				 const char *newlevel, bool parallel, bool going_down)
 | 
			
		||||
{
 | 
			
		||||
	pid_t pid;
 | 
			
		||||
	RC_STRING *service, *svc1, *svc2;
 | 
			
		||||
@@ -627,7 +623,7 @@ stop:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
do_start_services(bool parallel)
 | 
			
		||||
do_start_services(const RC_STRINGLIST *start_services, bool parallel)
 | 
			
		||||
{
 | 
			
		||||
	RC_STRING *service;
 | 
			
		||||
	pid_t pid;
 | 
			
		||||
@@ -754,6 +750,12 @@ main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	const char *bootlevel = NULL;
 | 
			
		||||
	char *newlevel = NULL;
 | 
			
		||||
	static RC_STRINGLIST *hotplugged_services;
 | 
			
		||||
	static RC_STRINGLIST *stop_services;
 | 
			
		||||
	static RC_STRINGLIST *start_services;
 | 
			
		||||
	static RC_STRINGLIST *types_n;
 | 
			
		||||
	static RC_STRINGLIST *types_nua;
 | 
			
		||||
	static RC_DEPTREE *deptree;
 | 
			
		||||
	RC_STRINGLIST *deporder = NULL;
 | 
			
		||||
	RC_STRINGLIST *tmplist;
 | 
			
		||||
	RC_STRING *service;
 | 
			
		||||
@@ -868,7 +870,11 @@ main(int argc, char **argv)
 | 
			
		||||
	snprintf(pidstr, sizeof(pidstr), "%d", getpid());
 | 
			
		||||
	setenv("RC_PID", pidstr, 1);
 | 
			
		||||
 | 
			
		||||
	/* Load current runlevel */
 | 
			
		||||
	/* Create a list of all services which should be started for the new or
 | 
			
		||||
	* current runlevel including those in boot, sysinit and hotplugged
 | 
			
		||||
	* runlevels.  Clearly, some of these will already be started so we
 | 
			
		||||
	* won't actually be starting them all.
 | 
			
		||||
	*/
 | 
			
		||||
	bootlevel = getenv("RC_BOOTLEVEL");
 | 
			
		||||
	runlevel = rc_runlevel_get();
 | 
			
		||||
 | 
			
		||||
@@ -972,8 +978,13 @@ main(int argc, char **argv)
 | 
			
		||||
		    applet, RC_STOPPING, strerror(errno));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Build a list of all services to stop and then work out the
 | 
			
		||||
	 * correct order for stopping them */
 | 
			
		||||
	/* Create a list of all services which we could stop (assuming
 | 
			
		||||
	* they won't be active in the new or current runlevel) including
 | 
			
		||||
	* all those services which have been started, are inactive or
 | 
			
		||||
	* are currently starting.  Clearly, some of these will be listed
 | 
			
		||||
	* in the new or current runlevel so we won't actually be stopping
 | 
			
		||||
	* them all.
 | 
			
		||||
	*/
 | 
			
		||||
	stop_services = rc_services_in_state(RC_SERVICE_STARTED);
 | 
			
		||||
	tmplist = rc_services_in_state(RC_SERVICE_INACTIVE);
 | 
			
		||||
	TAILQ_CONCAT(stop_services, tmplist, entries);
 | 
			
		||||
@@ -996,7 +1007,11 @@ main(int argc, char **argv)
 | 
			
		||||
		stop_services = tmplist;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Load our list of start services */
 | 
			
		||||
	/* Create a list of all services which should be started for the new or
 | 
			
		||||
	 * current runlevel including those in boot, sysinit and hotplugged
 | 
			
		||||
	 * runlevels.  Clearly, some of these will already be started so we
 | 
			
		||||
	 * won't actually be starting them all.
 | 
			
		||||
	 */
 | 
			
		||||
	hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED);
 | 
			
		||||
	start_services = rc_services_in_runlevel_stacked(newlevel ?
 | 
			
		||||
	    newlevel : runlevel);
 | 
			
		||||
@@ -1006,9 +1021,11 @@ main(int argc, char **argv)
 | 
			
		||||
		tmplist = rc_services_in_runlevel(RC_LEVEL_SYSINIT);
 | 
			
		||||
		TAILQ_CONCAT(start_services, tmplist, entries);
 | 
			
		||||
		free(tmplist);
 | 
			
		||||
		/* If we are NOT headed for the single-user runlevel... */
 | 
			
		||||
		if (strcmp(newlevel ? newlevel : runlevel,
 | 
			
		||||
			RC_LEVEL_SINGLE) != 0)
 | 
			
		||||
		{
 | 
			
		||||
			/* If we are NOT headed for the boot runlevel... */
 | 
			
		||||
			if (strcmp(newlevel ? newlevel : runlevel,
 | 
			
		||||
				bootlevel) != 0)
 | 
			
		||||
			{
 | 
			
		||||
@@ -1029,7 +1046,7 @@ main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
	/* Now stop the services that shouldn't be running */
 | 
			
		||||
	if (stop_services && !nostop)
 | 
			
		||||
		do_stop_services(newlevel, parallel, going_down);
 | 
			
		||||
		do_stop_services(types_n, start_services, stop_services, deptree, newlevel, parallel, going_down);
 | 
			
		||||
 | 
			
		||||
	/* Wait for our services to finish */
 | 
			
		||||
	wait_for_services();
 | 
			
		||||
@@ -1065,18 +1082,10 @@ main(int argc, char **argv)
 | 
			
		||||
		TAILQ_FOREACH(service, hotplugged_services, entries)
 | 
			
		||||
		    rc_service_mark(service->value, RC_SERVICE_HOTPLUGGED);
 | 
			
		||||
 | 
			
		||||
	/* Order the services to start */
 | 
			
		||||
	if (start_services) {
 | 
			
		||||
		rc_stringlist_sort(&start_services);
 | 
			
		||||
		deporder = rc_deptree_depends(deptree, types_nua,
 | 
			
		||||
		    start_services, runlevel,
 | 
			
		||||
		    depoptions | RC_DEP_START);
 | 
			
		||||
		rc_stringlist_free(start_services);
 | 
			
		||||
		start_services = deporder;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
	/* mark any services skipped as started */
 | 
			
		||||
	/* If the "noinit" parameter was passed on the kernel command line then
 | 
			
		||||
	 * mark the specified services as started so they will not be started
 | 
			
		||||
	 * by us. */
 | 
			
		||||
	proc = p = rc_proc_getent("noinit");
 | 
			
		||||
	if (proc) {
 | 
			
		||||
		while ((token = strsep(&p, ",")))
 | 
			
		||||
@@ -1085,19 +1094,38 @@ main(int argc, char **argv)
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* If we have a list of services to start then... */
 | 
			
		||||
	if (start_services) {
 | 
			
		||||
		do_start_services(parallel);
 | 
			
		||||
		/* FIXME: If we skip the boot runlevel and go straight
 | 
			
		||||
		 * to default from sysinit, we should now re-evaluate our
 | 
			
		||||
		 * start services + hotplugged services and call
 | 
			
		||||
		 * do_start_services a second time. */
 | 
			
		||||
		/* Get a list of the chained runlevels which compose the target runlevel */
 | 
			
		||||
		RC_STRINGLIST *runlevel_chain = rc_runlevel_stacks(runlevel);
 | 
			
		||||
 | 
			
		||||
		/* Wait for our services to finish */
 | 
			
		||||
		wait_for_services();
 | 
			
		||||
		/* Loop through them in reverse order. */
 | 
			
		||||
		RC_STRING *rlevel;
 | 
			
		||||
		TAILQ_FOREACH_REVERSE(rlevel, runlevel_chain, rc_stringlist, entries)
 | 
			
		||||
		{
 | 
			
		||||
			/* Get a list of all the services in that runlevel */
 | 
			
		||||
			RC_STRINGLIST *run_services = rc_services_in_runlevel(rlevel->value);
 | 
			
		||||
 | 
			
		||||
			/* Start those services. */
 | 
			
		||||
			rc_stringlist_sort(&run_services);
 | 
			
		||||
			deporder = rc_deptree_depends(deptree, types_nua, run_services, rlevel->value, depoptions | RC_DEP_START);
 | 
			
		||||
			rc_stringlist_free(run_services);
 | 
			
		||||
			run_services = deporder;
 | 
			
		||||
			do_start_services(run_services, parallel);
 | 
			
		||||
 | 
			
		||||
			/* Wait for our services to finish */
 | 
			
		||||
			wait_for_services();
 | 
			
		||||
			
 | 
			
		||||
			/* Free the list of services, we're done with it. */
 | 
			
		||||
			rc_stringlist_free(run_services);
 | 
			
		||||
		}
 | 
			
		||||
		rc_stringlist_free(runlevel_chain);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
	/* mark any services skipped as stopped */
 | 
			
		||||
	/* If the "noinit" parameter was passed on the kernel command line then
 | 
			
		||||
	 * mark the specified services as stopped so that our records reflect
 | 
			
		||||
	 * reality.	 */
 | 
			
		||||
	proc = p = rc_proc_getent("noinit");
 | 
			
		||||
	if (proc) {
 | 
			
		||||
		while ((token = strsep(&p, ",")))
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user