summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDan Nicholson <dbn.lists@gmail.com>2008-05-22 09:02:44 -0700
committerDan Nicholson <dbn.lists@gmail.com>2008-05-22 09:10:44 -0700
commitc136a3765efc354080aee77a7a8c64bdccadc0e4 (patch)
tree2f01d89a4b631faf8251e1402f5b831b6125d441 /lib
parent11920a5b64435f869aa920f33c135b7920a4df99 (diff)
Add functions for installing and removing init.d symlinks
This adds the initd_installrm_links function and its helpers for installing and removing symlinks. The function takes an ordered start or kill list and the init.d directory to operate on. There is just a single entry point since the determination of removal or addition is done in the dependency ordering.
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/installrm.c232
-rw-r--r--lib/installrm.h11
3 files changed, 244 insertions, 1 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 79caf38..a211c9d 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,3 +1,3 @@
noinst_LTLIBRARIES = libinitd.la
libinitd_la_SOURCES = str.h str.c strarg.c types.h parse.c active.c \
- initd.h initd.c initd-list.c rdep.h rdep.c
+ initd.h initd.c initd-list.c rdep.h rdep.c installrm.h installrm.c
diff --git a/lib/installrm.c b/lib/installrm.c
new file mode 100644
index 0000000..d0549b3
--- /dev/null
+++ b/lib/installrm.c
@@ -0,0 +1,232 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <error.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <glob.h>
+#include "installrm.h"
+#include "rcint.h"
+
+static bool installrm_verbose = false;
+static void install_level_links(const initd_list_t *ilp,
+ const struct rcpair *rcp, initd_sk_t sk);
+static void remove_existing_link(const initd_t *ip, const char *rdir,
+ initd_sk_t sk);
+static void install_new_link(const initd_t *ip, const struct rcpair *rcp,
+ initd_sk_t sk, int *prio);
+static void set_current_priority(initd_sk_t sk, int *prio);
+
+bool initd_installrm_links(const initd_list_t *ilp, const char *dir,
+ initd_sk_t sk)
+{
+ bool ret = false;
+ char *cwd;
+ struct rcpair *cur;
+ struct stat sbuf;
+
+ if (!ilp)
+ goto out;
+
+ /* move to the init.d directory */
+ cwd = get_current_dir_name();
+ if (chdir(dir) != 0)
+ error(1, errno, "%s", dir);
+
+ /* See if we can can find an existing sysinit directory */
+ for (cur = rcsdirs; cur->dir; cur++) {
+ if (stat(cur->dir, &sbuf) != 0) {
+ if (errno != ENOENT)
+ error(1, errno, "%s", cur->dir);
+ } else {
+ /* found one */
+ break;
+ }
+ }
+
+ /* Fallback if no existing sysinit directory found */
+ if (!cur->dir)
+ cur = rcsdirs;
+
+ /* Write the links for the sysinit directory */
+ install_level_links(ilp, cur, sk);
+
+ /* Loop over the rest of the directories */
+ for (cur = rcdirs; cur->dir; cur++)
+ install_level_links(ilp, cur, sk);
+
+ /* Return to the original directory */
+ if (chdir(cwd) != 0)
+ error(1, errno, "%s", cwd);
+ free(cwd);
+
+ ret = true;
+out:
+ return ret;
+}
+
+void initd_installrm_set_verbose(bool verbose)
+{
+ installrm_verbose = verbose;
+}
+
+static void install_level_links(const initd_list_t *ilp,
+ const struct rcpair *rcp, initd_sk_t sk)
+{
+ initd_t *ip;
+ initd_rc_t rc = rcp->rc;
+ char *dir = rcp->dir;
+
+ /* Counters for next link (S20foo, S21bar, ...). Start at 0
+ * and count up for start links, start at 99 and count down
+ * for stop links. */
+ int sk_prio = (sk == SK_START) ? 0 : 99;
+
+#if 0
+ if (installrm_verbose) {
+ fprintf(stderr, "Installing %s links in %s\n",
+ (sk == SK_START) ? "start" : "stop", dir);
+ }
+#endif
+
+ /* Change to the rc directory */
+ if (chdir(dir) != 0)
+ error(1, errno, "%s", dir);
+
+ /* First remove existing links, beginning with the head of the
+ * list for start links. */
+ if (sk == SK_START) {
+ for (ip = ilp->first; ip; ip = ip->next) {
+ /* skip script if not active in this level */
+ if (!initd_is_active(ip, rc, sk))
+ continue;
+
+ remove_existing_link(ip, dir, sk);
+ }
+ } else {
+ for (ip = ilp->last; ip; ip = ip->prev) {
+ /* skip script if not active in this level */
+ if (!initd_is_active(ip, rc, sk))
+ continue;
+
+ remove_existing_link(ip, dir, sk);
+ }
+ }
+
+ /* Then install new links */
+ if (sk == SK_START) {
+ for (ip = ilp->first; ip; ip = ip->next)
+ install_new_link(ip, rcp, sk, &sk_prio);
+ } else {
+ for (ip = ilp->last; ip; ip = ip->prev)
+ install_new_link(ip, rcp, sk, &sk_prio);
+ }
+}
+
+static void remove_existing_link(const initd_t *ip, const char *rdir,
+ initd_sk_t sk)
+{
+ strarg_t *links;
+ int n, nlinks;
+
+ if (sk == SK_START)
+ links = ip->astart_links;
+ else
+ links = ip->astop_links;
+
+ /* See if any of the links exist in this directory */
+ nlinks = strarg_get_num(links);
+ for (n = 0; n < nlinks; n++) {
+ char *cur = strarg_get_str(links, n);
+ if (strncmp(cur, rdir, strlen(rdir)) == 0) {
+ /* Remove it, stripping the leading dir + /.
+ * Bomb for any error besides nonexistent file */
+ char *path = cur + strlen(rdir) + 1;
+ if (installrm_verbose) {
+ char *bdir = strrchr(rdir, '/') + 1;
+ fprintf(stderr, "Removing link %s in %s\n",
+ path, bdir);
+ }
+ if (remove(path) != 0) {
+ if (errno != ENOENT)
+ error(1, errno, "%s", path);
+ }
+ }
+ }
+}
+
+static void install_new_link(const initd_t *ip, const struct rcpair *rcp,
+ initd_sk_t sk, int *prio)
+{
+ char target[PATH_MAX];
+ char path[PATH_MAX];
+ char *tname;
+ char skc;
+ initd_rc_t rc, match;
+
+ if (!ip || !rcp)
+ return;
+
+ /* Check if this service should be added to this level */
+ rc = rcp->rc;
+ match = (sk == SK_START) ? ip->dstart : ip->dstop;
+ if (!(match & rc))
+ return;
+
+ /* Set the current priority */
+ set_current_priority(sk, prio);
+
+ /* Set the name for the link [SK][0-9][0-9]name */
+ tname = ip->name;
+ skc = (sk == SK_START) ? 'S' : 'K';
+ snprintf(path, PATH_MAX, "%c%02d%s", skc, *prio, tname);
+
+ /* Construct the target */
+ snprintf(target, PATH_MAX, "../init.d/%s", tname);
+
+ /* Create the symlink */
+ if (installrm_verbose) {
+ char *bdir = strrchr(rcp->dir, '/') + 1;
+ fprintf(stderr, "Creating link %s -> %s in %s\n",
+ path, target, bdir);
+ }
+ if (symlink(target, path) < 0)
+ error(1, errno, "%s -> %s", path, target);
+}
+
+#define PRIOPATLEN 17
+
+static void set_current_priority(initd_sk_t sk, int *prio)
+{
+ char pat[PRIOPATLEN];
+ char skc;
+ glob_t globbuf;
+
+ /* We can only handle priorities from 0-99 */
+ if (sk == SK_START) {
+ if (*prio >= 99)
+ return;
+ } else {
+ if (*prio <= 0)
+ return;
+ }
+
+ do {
+ /* Setup a glob pattern ([SK][0-9][0-9][![:digit:]]*) */
+ skc = (sk == SK_START) ? 'S' : 'K';
+ snprintf(pat, PRIOPATLEN, "%c%02d[![:digit:]]*",
+ skc, *prio);
+
+ /* Try to glob it and break if none found */
+ glob(pat, 0, NULL, &globbuf);
+ if (!globbuf.gl_pathc)
+ break;
+ } while (((sk == SK_START) && (++(*prio) < 99)) ||
+ ((sk == SK_STOP) && (--(*prio) > 0)));
+}
diff --git a/lib/installrm.h b/lib/installrm.h
new file mode 100644
index 0000000..75991d8
--- /dev/null
+++ b/lib/installrm.h
@@ -0,0 +1,11 @@
+#ifndef _installrm_h_
+#define _installrm_h_
+
+#include <stdbool.h>
+#include "initd.h"
+
+extern bool initd_installrm_links(const initd_list_t *ilp,
+ const char *dir, initd_sk_t sk);
+extern void initd_installrm_set_verbose(bool verbose);
+
+#endif /* _installrm_h_ */