Add runlevel stacking, #88

This implementation has the limitation that you cannot have a stacked
runlevel and service of the same name in a runlevel.
This commit is contained in:
Roy Marples
2009-05-02 12:26:45 +01:00
parent e040bd77e9
commit 6615eb4b68
11 changed files with 217 additions and 26 deletions

View File

@@ -66,7 +66,7 @@ static const rc_service_state_name_t rc_service_state_names[] = {
};
#define LS_INITD 0x01
#define LS_DIR 0x02
#define LS_DIR 0x02
static RC_STRINGLIST *
ls_dir(const char *dir, int options)
{
@@ -102,7 +102,7 @@ ls_dir(const char *dir, int options)
}
if (options & LS_DIR) {
if (stat(d->d_name, &buf) == 0 &&
! S_ISDIR(buf.st_mode))
!S_ISDIR(buf.st_mode))
continue;
}
rc_stringlist_add(list, d->d_name);
@@ -330,6 +330,51 @@ rc_runlevel_exists(const char *runlevel)
}
librc_hidden_def(rc_runlevel_exists)
bool
rc_runlevel_stack(const char *dst, const char *src)
{
char d[PATH_MAX], s[PATH_MAX];
if (!rc_runlevel_exists(dst) || !rc_runlevel_exists(src))
return false;
snprintf(s, sizeof(s), "../%s", src);
snprintf(d, sizeof(s), "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
return (symlink(s, d) == 0 ? true : false);
}
librc_hidden_def(rc_runlevel_stack)
bool
rc_runlevel_unstack(const char *dst, const char *src)
{
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
return (unlink(path) == 0 ? true : false);
}
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;
}
librc_hidden_def(rc_runlevel_stacks)
/* Resolve a service name to it's full path */
char *
rc_service_resolve(const char *service)
@@ -780,6 +825,27 @@ rc_services_in_runlevel(const char *runlevel)
}
librc_hidden_def(rc_services_in_runlevel)
RC_STRINGLIST *
rc_services_in_runlevel_stacked(const char *runlevel)
{
RC_STRINGLIST *list, *stacks, *sl;
RC_STRING *stack;
list = rc_services_in_runlevel(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;
}
return list;
}
librc_hidden_def(rc_services_in_runlevel_stacked)
RC_STRINGLIST *
rc_services_in_state(RC_SERVICE state)
{

View File

@@ -91,8 +91,11 @@ librc_hidden_proto(rc_runlevel_exists)
librc_hidden_proto(rc_runlevel_get)
librc_hidden_proto(rc_runlevel_list)
librc_hidden_proto(rc_runlevel_set)
librc_hidden_proto(rc_runlevel_stack)
librc_hidden_proto(rc_runlevel_stacks)
librc_hidden_proto(rc_runlevel_starting)
librc_hidden_proto(rc_runlevel_stopping)
librc_hidden_proto(rc_runlevel_unstack)
librc_hidden_proto(rc_service_add)
librc_hidden_proto(rc_service_daemons_crashed)
librc_hidden_proto(rc_service_daemon_set)
@@ -106,6 +109,7 @@ librc_hidden_proto(rc_service_resolve)
librc_hidden_proto(rc_service_schedule_clear)
librc_hidden_proto(rc_service_schedule_start)
librc_hidden_proto(rc_services_in_runlevel)
librc_hidden_proto(rc_services_in_runlevel_stacked)
librc_hidden_proto(rc_services_in_state)
librc_hidden_proto(rc_services_scheduled)
librc_hidden_proto(rc_services_scheduled_by)

View File

@@ -80,6 +80,22 @@ char *rc_runlevel_get(void);
* @return true if the runlevel exists, otherwise false */
bool rc_runlevel_exists(const char *);
/*! Stack a runlevel onto another
* @param runlevel to stack onto
* @param runlevel being stacked
* @return true if successful, otherwise false */
bool rc_runlevel_stack(const char *, const char *);
/*! Unstack a runlevel from another
* @param runlevel to unstack from
* @param runlevel being unstacked
* @return true if successful, otherwise false */
bool rc_runlevel_unstack(const char *, const char *);
/*! Return a NULL terminated list of runlevel stacks in the runlevels
* @return a NULL terminated list of runlevels */
RC_STRINGLIST *rc_runlevel_stacks(const char *);
/*! Return a NULL terminated list of runlevels
* @return a NULL terminated list of runlevels */
RC_STRINGLIST *rc_runlevel_list(void);
@@ -225,6 +241,11 @@ bool rc_service_value_set(const char *, const char *, const char *);
* @return NULL terminated list of services */
RC_STRINGLIST *rc_services_in_runlevel(const char *);
/*! List the stacked services in a runlevel
* @param runlevel to list
* @return NULL terminated list of services */
RC_STRINGLIST *rc_services_in_runlevel_stacked(const char *);
/*! List the services in a state
* @param state to list
* @return NULL terminated list of services */

View File

@@ -18,8 +18,11 @@ global:
rc_runlevel_get;
rc_runlevel_list;
rc_runlevel_set;
rc_runlevel_stack;
rc_runlevel_stacks;
rc_runlevel_starting;
rc_runlevel_stopping;
rc_runlevel_unstack;
rc_service_add;
rc_service_daemons_crashed;
rc_service_daemon_set;
@@ -34,6 +37,7 @@ global:
rc_service_schedule_clear;
rc_service_schedule_start;
rc_services_in_runlevel;
rc_services_in_runlevel_stacked;
rc_services_in_state;
rc_services_scheduled;
rc_services_scheduled_by;

View File

@@ -78,8 +78,10 @@ _rc_can_find_pids(void)
}
static void
print_level(const char *level)
print_level(const char *prefix, const char *level)
{
if (prefix)
printf("%s ", prefix);
printf ("Runlevel: ");
if (isatty(fileno(stdout)))
printf("%s%s%s\n",
@@ -274,16 +276,28 @@ rc_status(int argc, char **argv)
deptree = _rc_deptree_load(0, NULL);
TAILQ_FOREACH(l, levels, entries) {
print_level(l->value);
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;
rc_stringlist_free(nservices);
nservices = NULL;
rc_stringlist_free(services);
services = NULL;
}
if (aflag || argc < 2) {
/* Show hotplugged services */
print_level("hotplugged");
print_level("Dynamic", "hotplugged");
services = rc_services_in_state(RC_SERVICE_HOTPLUGGED);
print_services(NULL, services);
rc_stringlist_free(services);
@@ -336,9 +350,9 @@ rc_status(int argc, char **argv)
rc_stringlist_free(tmp);
}
l->value = p;
print_level("needed");
print_level("Dynamic", "needed");
print_services(NULL, nservices);
print_level("manual");
print_level("Dynamic", "manual");
print_services(NULL, services);
}

View File

@@ -63,11 +63,11 @@ add(const char *runlevel, const char *service)
eerror("%s: service `%s' does not exist",
applet, service);
} else if (rc_service_in_runlevel(service, runlevel)) {
ewarn ("%s: %s already installed in runlevel `%s'; skipping",
ewarn("%s: %s already installed in runlevel `%s'; skipping",
applet, service, runlevel);
retval = 0;
} else if (rc_service_add(runlevel, service)) {
einfo ("%s added to runlevel %s", service, runlevel);
einfo("service %s added to runlevel %s", service, runlevel);
retval = 1;
} else
eerror("%s: failed to add service `%s' to runlevel `%s': %s",
@@ -83,20 +83,76 @@ delete(const char *runlevel, const char *service)
errno = 0;
if (rc_service_delete(runlevel, service)) {
einfo("%s removed from runlevel %s", service, runlevel);
einfo("service %s removed from runlevel %s",
service, runlevel);
return 1;
}
if (errno == ENOENT)
eerror ("%s: service `%s' is not in the runlevel `%s'",
eerror("%s: service `%s' is not in the runlevel `%s'",
applet, service, runlevel);
else
eerror ("%s: failed to remove service `%s' from runlevel `%s': %s",
eerror("%s: failed to remove service `%s' from runlevel `%s': %s",
applet, service, runlevel, strerror (errno));
return retval;
}
static int
addstack(const char *runlevel, const char *stack)
{
if (!rc_runlevel_exists(runlevel)) {
eerror("%s: runlevel `%s' does not exist", applet, runlevel);
return -1;
}
if (!rc_runlevel_exists(stack)) {
eerror("%s: runlevel `%s' does not exist", applet, stack);
return -1;
}
if (strcmp(runlevel, stack) == 0) {
eerror("%s: cannot stack `%s' onto itself", applet, stack);
return -1;
}
if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 ||
strcmp(stack, RC_LEVEL_SYSINIT) == 0 ||
strcmp(runlevel, RC_LEVEL_BOOT) == 0 ||
strcmp(stack, RC_LEVEL_BOOT) == 0 ||
strcmp(runlevel, RC_LEVEL_SINGLE) == 0 ||
strcmp(stack, RC_LEVEL_SINGLE) == 0 ||
strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
strcmp(stack, RC_LEVEL_SHUTDOWN) == 0)
{
eerror("%s: cannot stack the %s runlevel",
applet, RC_LEVEL_SYSINIT);
return -1;
}
if (!rc_runlevel_stack(runlevel, stack)) {
eerror("%s: failed to stack `%s' to `%s': %s",
applet, stack, runlevel, strerror(errno));
return -1;
}
einfo("runlevel %s added to runlevel %s", stack, runlevel);
return 1;
}
static int
delstack(const char *runlevel, const char *stack)
{
if (rc_runlevel_unstack(runlevel, stack)) {
einfo("runlevel %s removed from runlevel %s", stack, runlevel);
return 1;
}
if (errno == ENOENT)
eerror("%s: runlevel `%s' is not in the runlevel `%s'",
applet, stack, runlevel);
else
eerror("%s: failed to remove runlevel `%s' from runlevel `%s': %s",
applet, stack, runlevel, strerror (errno));
return -1;
}
static void
show(RC_STRINGLIST *runlevels, bool verbose)
{
@@ -143,12 +199,14 @@ show(RC_STRINGLIST *runlevels, bool verbose)
"Usage: rc-update [options] add service <runlevel>\n" \
" rc-update [options] del service <runlevel>\n" \
" rc-update [options] show"
#define getoptstring "u" getoptstring_COMMON
#define getoptstring "su" getoptstring_COMMON
static const struct option longopts[] = {
{ "stack", 0, NULL, 's' },
{ "update", 0, NULL, 'u' },
longopts_COMMON
};
static const char * const longopts_help[] = {
"Stack a runlevel instead of a service",
"Force an update of the dependency tree",
longopts_help_COMMON
};
@@ -166,7 +224,7 @@ rc_update(int argc, char **argv)
char *service = NULL;
char *p;
int action = 0;
bool verbose = false;
bool verbose = false, stack = false;
int opt;
int retval = EXIT_FAILURE;
int num_updated = 0;
@@ -176,11 +234,14 @@ rc_update(int argc, char **argv)
while ((opt = getopt_long(argc, argv, getoptstring,
longopts, (int *)0)) != -1)
switch (opt) {
case 's':
stack = true;
break;
case 'u':
_rc_deptree_load(-1, &ret);
return ret;
case_RC_COMMON_GETOPT
}
case_RC_COMMON_GETOPT;
}
verbose = rc_yesno(getenv ("EINFO_VERBOSE"));
@@ -241,9 +302,9 @@ rc_update(int argc, char **argv)
eerror ("%s: no service specified", applet);
else {
if (action & DOADD) {
actfunc = add;
actfunc = stack ? addstack : add;
} else if (action & DODELETE) {
actfunc = delete;
actfunc = stack ? delstack : delete;
} else {
rc_stringlist_free(runlevels);
eerrorx("%s: invalid action", applet);

View File

@@ -1037,7 +1037,7 @@ main(int argc, char **argv)
/* Load our list of start services */
hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED);
start_services = rc_services_in_runlevel(newlevel ?
start_services = rc_services_in_runlevel_stacked(newlevel ?
newlevel : runlevel);
if (strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 &&
strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SYSINIT) != 0)

View File

@@ -16,8 +16,11 @@ rc_runlevel_exists
rc_runlevel_get
rc_runlevel_list
rc_runlevel_set
rc_runlevel_stack
rc_runlevel_stacks
rc_runlevel_starting
rc_runlevel_stopping
rc_runlevel_unstack
rc_service_add
rc_service_daemon_set
rc_service_daemons_crashed
@@ -35,6 +38,7 @@ rc_service_state
rc_service_value_get
rc_service_value_set
rc_services_in_runlevel
rc_services_in_runlevel_stacked
rc_services_in_state
rc_services_scheduled
rc_services_scheduled_by

View File

@@ -32,10 +32,16 @@ rc_runlevel_list
rc_runlevel_list@@RC_1.0
rc_runlevel_set
rc_runlevel_set@@RC_1.0
rc_runlevel_stack
rc_runlevel_stack@@RC_1.0
rc_runlevel_stacks
rc_runlevel_stacks@@RC_1.0
rc_runlevel_starting
rc_runlevel_starting@@RC_1.0
rc_runlevel_stopping
rc_runlevel_stopping@@RC_1.0
rc_runlevel_unstack
rc_runlevel_unstack@@RC_1.0
rc_service_add
rc_service_add@@RC_1.0
rc_service_daemon_set
@@ -70,6 +76,8 @@ rc_service_value_set
rc_service_value_set@@RC_1.0
rc_services_in_runlevel
rc_services_in_runlevel@@RC_1.0
rc_services_in_runlevel_stacked
rc_services_in_runlevel_stacked@@RC_1.0
rc_services_in_state
rc_services_in_state@@RC_1.0
rc_services_scheduled