diff options
author | Dan Nicholson <dbn.lists@gmail.com> | 2008-05-22 09:02:44 -0700 |
---|---|---|
committer | Dan Nicholson <dbn.lists@gmail.com> | 2008-05-22 09:10:44 -0700 |
commit | c136a3765efc354080aee77a7a8c64bdccadc0e4 (patch) | |
tree | 2f01d89a4b631faf8251e1402f5b831b6125d441 /lib | |
parent | 11920a5b64435f869aa920f33c135b7920a4df99 (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.am | 2 | ||||
-rw-r--r-- | lib/installrm.c | 232 | ||||
-rw-r--r-- | lib/installrm.h | 11 |
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_ */ |