aboutsummaryrefslogtreecommitdiff
path: root/src/rc-update
diff options
context:
space:
mode:
Diffstat (limited to 'src/rc-update')
-rw-r--r--src/rc-update/meson.build7
-rw-r--r--src/rc-update/rc-update.c353
2 files changed, 360 insertions, 0 deletions
diff --git a/src/rc-update/meson.build b/src/rc-update/meson.build
new file mode 100644
index 00000000..7d284979
--- /dev/null
+++ b/src/rc-update/meson.build
@@ -0,0 +1,7 @@
+executable('rc-update',
+ ['rc-update.c', misc_c, usage_c, version_h],
+ c_args : cc_branding_flags,
+ link_with: [libeinfo, librc],
+ include_directories: [incdir, einfo_incdir, rc_incdir],
+ install: true,
+ install_dir: sbindir)
diff --git a/src/rc-update/rc-update.c b/src/rc-update/rc-update.c
new file mode 100644
index 00000000..445db4e8
--- /dev/null
+++ b/src/rc-update/rc-update.c
@@ -0,0 +1,353 @@
+/*
+ * rc-update
+ * Manage init scripts and runlevels
+ */
+
+/*
+ * Copyright (c) 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "einfo.h"
+#include "queue.h"
+#include "rc.h"
+#include "misc.h"
+#include "_usage.h"
+
+const char *applet = NULL;
+const char *extraopts = NULL;
+const char *usagestring = "" \
+ "Usage: rc-update [options] add <service> [<runlevel>...]\n" \
+ " or: rc-update [options] del <service> [<runlevel>...]\n" \
+ " or: rc-update [options] [show [<runlevel>...]]";
+const char getoptstring[] = "asu" getoptstring_COMMON;
+const struct option longopts[] = {
+ { "all", 0, NULL, 'a' },
+ { "stack", 0, NULL, 's' },
+ { "update", 0, NULL, 'u' },
+ longopts_COMMON
+};
+const char * const longopts_help[] = {
+ "Process all runlevels",
+ "Stack a runlevel instead of a service",
+ "Force an update of the dependency tree",
+ longopts_help_COMMON
+};
+
+/* Return the number of changes made:
+ * -1 = no changes (error)
+ * 0 = no changes (nothing to do)
+ * 1+ = number of runlevels updated
+ */
+static int
+add(const char *runlevel, const char *service)
+{
+ int retval = -1;
+
+ if (!rc_service_exists(service)) {
+ if (errno == ENOEXEC)
+ eerror("%s: service `%s' is not executable",
+ applet, service);
+ else
+ eerror("%s: service `%s' does not exist",
+ applet, service);
+ } else if (rc_service_in_runlevel(service, runlevel)) {
+ einfo("%s: %s already installed in runlevel `%s'; skipping",
+ applet, service, runlevel);
+ retval = 0;
+ } else if (rc_service_add(runlevel, service)) {
+ einfo("service %s added to runlevel %s", service, runlevel);
+ retval = 1;
+ } else
+ eerror("%s: failed to add service `%s' to runlevel `%s': %s",
+ applet, service, runlevel, strerror (errno));
+
+ return retval;
+}
+
+static int
+delete(const char *runlevel, const char *service)
+{
+ int retval = -1;
+
+ errno = 0;
+ if (rc_service_delete(runlevel, service)) {
+ einfo("service %s removed from runlevel %s",
+ service, runlevel);
+ return 1;
+ }
+
+ if (errno == ENOENT)
+ 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",
+ 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)
+{
+ RC_STRINGLIST *services = rc_services_in_runlevel(NULL);
+ RC_STRING *service;
+ RC_STRING *runlevel;
+ RC_STRINGLIST *in;
+ bool inone;
+ char *buffer = NULL;
+ size_t l;
+
+ rc_stringlist_sort(&services);
+ TAILQ_FOREACH(service, services, entries) {
+ in = rc_stringlist_new();
+ inone = false;
+
+ TAILQ_FOREACH(runlevel, runlevels, entries) {
+ if (rc_service_in_runlevel(service->value,
+ runlevel->value))
+ {
+ rc_stringlist_add(in, runlevel->value);
+ inone = true;
+ } else {
+ l = strlen(runlevel->value);
+ buffer = xmalloc(l+1);
+ memset (buffer, ' ', l);
+ buffer[l] = 0;
+ rc_stringlist_add (in, buffer);
+ free(buffer);
+ }
+ }
+
+ if (inone || verbose) {
+ printf(" %20s |", service->value);
+ TAILQ_FOREACH(runlevel, in, entries)
+ printf (" %s", runlevel->value);
+ printf ("\n");
+ }
+ rc_stringlist_free(in);
+ }
+
+ rc_stringlist_free (services);
+}
+
+#define DOADD (1 << 1)
+#define DODELETE (1 << 2)
+#define DOSHOW (1 << 3)
+
+int main(int argc, char **argv)
+{
+ RC_DEPTREE *deptree;
+ RC_STRINGLIST *runlevels;
+ RC_STRING *runlevel;
+ char *service = NULL;
+ char *p;
+ int action = 0;
+ bool verbose = false, stack = false, all_runlevels = false;
+ int opt;
+ int retval = EXIT_FAILURE;
+ int num_updated = 0;
+ int (*actfunc)(const char *, const char *);
+ int ret;
+
+ applet = basename_c(argv[0]);
+ while ((opt = getopt_long(argc, argv, getoptstring,
+ longopts, (int *)0)) != -1)
+ switch (opt) {
+ case 'a':
+ all_runlevels = true;
+ break;
+ case 's':
+ stack = true;
+ break;
+ case 'u':
+ deptree = _rc_deptree_load(-1, &ret);
+ if (deptree)
+ rc_deptree_free(deptree);
+ return ret;
+ case_RC_COMMON_GETOPT
+ }
+
+ verbose = rc_yesno(getenv ("EINFO_VERBOSE"));
+
+ if ((action & DOSHOW && action != DOSHOW) ||
+ (action & DOADD && action != DOADD) ||
+ (action & DODELETE && action != DODELETE))
+ eerrorx("%s: cannot mix commands", applet);
+
+ /* We need to be backwards compatible */
+ if (optind < argc) {
+ if (strcmp(argv[optind], "add") == 0)
+ action = DOADD;
+ else if (strcmp(argv[optind], "delete") == 0 ||
+ strcmp(argv[optind], "del") == 0)
+ action = DODELETE;
+ else if (strcmp(argv[optind], "show") == 0)
+ action = DOSHOW;
+ if (action)
+ optind++;
+ else
+ eerrorx("%s: invalid command `%s'",
+ applet, argv[optind]);
+ }
+ if (!action)
+ action = DOSHOW;
+
+ runlevels = rc_stringlist_new();
+
+ if (optind >= argc) {
+ if (!(action & DOSHOW))
+ eerrorx("%s: no service specified", applet);
+ } else {
+ service = argv[optind];
+ optind++;
+
+ while (optind < argc)
+ if (rc_runlevel_exists(argv[optind]))
+ rc_stringlist_add(runlevels, argv[optind++]);
+ else {
+ rc_stringlist_free(runlevels);
+ eerrorx ("%s: `%s' is not a valid runlevel",
+ applet, argv[optind]);
+ }
+ }
+
+ retval = EXIT_SUCCESS;
+ if (action & DOSHOW) {
+ if (service)
+ rc_stringlist_add(runlevels, service);
+ if (!TAILQ_FIRST(runlevels)) {
+ free(runlevels);
+ runlevels = rc_runlevel_list();
+ }
+
+ rc_stringlist_sort(&runlevels);
+ show (runlevels, verbose);
+ } else {
+ if (!service)
+ eerror ("%s: no service specified", applet);
+ else {
+ if (action & DOADD) {
+ if (all_runlevels) {
+ rc_stringlist_free(runlevels);
+ eerrorx("%s: the -a option is invalid with add", applet);
+ }
+ actfunc = stack ? addstack : add;
+ } else if (action & DODELETE) {
+ actfunc = stack ? delstack : delete;
+ } else {
+ rc_stringlist_free(runlevels);
+ eerrorx("%s: invalid action", applet);
+ }
+
+ if (!TAILQ_FIRST(runlevels)) {
+ if (all_runlevels) {
+ free(runlevels);
+ runlevels = rc_runlevel_list();
+ } else {
+ p = rc_runlevel_get();
+ rc_stringlist_add(runlevels, p);
+ free(p);
+ }
+ }
+
+ if (!TAILQ_FIRST(runlevels)) {
+ free(runlevels);
+ eerrorx("%s: no runlevels found", applet);
+ }
+
+ TAILQ_FOREACH(runlevel, runlevels, entries) {
+ if (!rc_runlevel_exists(runlevel->value)) {
+ eerror ("%s: runlevel `%s' does not exist",
+ applet, runlevel->value);
+ continue;
+ }
+
+ ret = actfunc(runlevel->value, service);
+ if (ret < 0)
+ retval = EXIT_FAILURE;
+ num_updated += ret;
+ }
+
+ if (retval == EXIT_SUCCESS &&
+ num_updated == 0 && action & DODELETE)
+ ewarnx("%s: service `%s' not found in any"
+ " of the specified runlevels",
+ applet, service);
+ }
+ }
+
+ rc_stringlist_free(runlevels);
+ return retval;
+}