aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2009-05-02 12:26:45 +0100
committerRoy Marples <roy@marples.name>2009-05-02 12:26:45 +0100
commit6615eb4b689d7aa1d047bd9ed75eca80beac9767 (patch)
treeefc5a4dee97fd4737599f1f4f71bad7a8ba7f470
parente040bd77e983e609fea5a87549b58e81b96e4276 (diff)
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.
-rw-r--r--man/rc-update.814
-rw-r--r--man/rc.89
-rw-r--r--src/librc/librc.c70
-rw-r--r--src/librc/librc.h4
-rw-r--r--src/librc/rc.h.in21
-rw-r--r--src/librc/rc.map4
-rw-r--r--src/rc/rc-status.c24
-rw-r--r--src/rc/rc-update.c83
-rw-r--r--src/rc/rc.c2
-rw-r--r--src/test/librc.funcs.hidden.list4
-rw-r--r--src/test/rc.funcs.list8
11 files changed, 217 insertions, 26 deletions
diff --git a/man/rc-update.8 b/man/rc-update.8
index 6cad29e5..6fad02c4 100644
--- a/man/rc-update.8
+++ b/man/rc-update.8
@@ -1,4 +1,4 @@
-.\" Copyright (c) 2007-2009 Roy Marples
+4.\" Copyright (c) 2007-2009 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd Jan 10, 2009
+.Dd May 2, 2009
.Dt RC-UPDATE 8 SMM
.Os OpenRC
.Sh NAME
@@ -30,10 +30,12 @@
.Nd add and remove services to and from a runlevel
.Sh SYNOPSIS
.Nm
+.Op Fl s , -stack
.Ar add
.Ar service
.Op Ar runlevel ...
.Nm
+.Op Fl s , -stack
.Ar delete
.Ar service
.Op Ar runlevel ...
@@ -51,7 +53,8 @@ All services must reside in the
.Pa /etc/init.d
or
.Pa /usr/local/etc/init.d
-directories. They must also conform to the OpenRC runscript standard.
+directories.
+They must also conform to the OpenRC runscript standard.
.Pp
.Bl -tag -width "Fl a , -delete service"
.It Ar add Ar service
@@ -78,6 +81,11 @@ Forces an update of the dependency tree cache.
This may be needed in the event of clock skew (a file in /etc is newer than the
system clock).
.El
+.Pp
+If the
+.Fl s , -stack
+option is given then we either add or remove the runlevel from the runlevel.
+This allows inheritance of runlevels.
.Sh SEE ALSO
.Xr rc 8 ,
.Xr rc-status 8
diff --git a/man/rc.8 b/man/rc.8
index 1e97762b..ab161fe7 100644
--- a/man/rc.8
+++ b/man/rc.8
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd January 13, 2009
+.Dd May 2, 2009
.Dt RC 8 SMM
.Os OpenRC
.Sh NAME
@@ -35,10 +35,11 @@
.Sh DESCRIPTION
.Nm
first stops any services that are not for the runlevel and then starts any
-services added by
+services in the runlevel and from stacked runlevels added by
.Nm rc-update
-that are not currently started. If no runlevel is specified then we use the
-current runlevel the system is currently in.
+that are not currently started.
+If no runlevel is specified then we use the current runlevel the system
+is currently in.
.Pp
There are some special runlevels that you should be aware of:
.Bl -tag -width "shutdown"
diff --git a/src/librc/librc.c b/src/librc/librc.c
index 05c1c3d0..f73936d9 100644
--- a/src/librc/librc.c
+++ b/src/librc/librc.c
@@ -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)
@@ -781,6 +826,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)
{
RC_STRINGLIST *services;
diff --git a/src/librc/librc.h b/src/librc/librc.h
index ba7da315..d2501c06 100644
--- a/src/librc/librc.h
+++ b/src/librc/librc.h
@@ -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)
diff --git a/src/librc/rc.h.in b/src/librc/rc.h.in
index 82874156..cb5258c5 100644
--- a/src/librc/rc.h.in
+++ b/src/librc/rc.h.in
@@ -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 */
diff --git a/src/librc/rc.map b/src/librc/rc.map
index e9fed69b..7cfbfabc 100644
--- a/src/librc/rc.map
+++ b/src/librc/rc.map
@@ -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;
diff --git a/src/rc/rc-status.c b/src/rc/rc-status.c
index 4c6e5129..0a232777 100644
--- a/src/rc/rc-status.c
+++ b/src/rc/rc-status.c
@@ -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);
}
diff --git a/src/rc/rc-update.c b/src/rc/rc-update.c
index f37f6f02..46adc635 100644
--- a/src/rc/rc-update.c
+++ b/src/rc/rc-update.c
@@ -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);
diff --git a/src/rc/rc.c b/src/rc/rc.c
index 78cfcae4..8e40e1d9 100644
--- a/src/rc/rc.c
+++ b/src/rc/rc.c
@@ -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)
diff --git a/src/test/librc.funcs.hidden.list b/src/test/librc.funcs.hidden.list
index 2416a0c2..121bfcf0 100644
--- a/src/test/librc.funcs.hidden.list
+++ b/src/test/librc.funcs.hidden.list
@@ -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
diff --git a/src/test/rc.funcs.list b/src/test/rc.funcs.list
index f6324741..409ede0d 100644
--- a/src/test/rc.funcs.list
+++ b/src/test/rc.funcs.list
@@ -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