summaryrefslogtreecommitdiff
path: root/listing.c
diff options
context:
space:
mode:
authorDmitry Bogatov <KAction@debian.org>2019-09-28 01:02:45 +0100
committerDmitry Bogatov <KAction@debian.org>2019-09-28 01:02:45 +0100
commit4d8459ef873ca4f90ded1a6bab58a53f0ee87e6c (patch)
tree321432239f40aaa31a5a4dd0add04b434bd54484 /listing.c
Import insserv_1.21.0.orig.tar.xz
[dgit import orig insserv_1.21.0.orig.tar.xz]
Diffstat (limited to 'listing.c')
-rw-r--r--listing.c1279
1 files changed, 1279 insertions, 0 deletions
diff --git a/listing.c b/listing.c
new file mode 100644
index 0000000..dca331b
--- /dev/null
+++ b/listing.c
@@ -0,0 +1,1279 @@
+/*
+ * listing.c
+ *
+ * Copyright 2000-2009 Werner Fink, 2000 SuSE GmbH Nuernberg, Germany,
+ * 2003 SuSE Linux AG, Germany.
+ * 2007-2009 SuSE Linux Products GmbH Nuernberg, Germany
+ *
+ * This source is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include "listing.h"
+
+int maxstart = 0; /* Maximum start order of runlevels 0 upto 6 and S */
+int maxstop = 0; /* Maximum stop order of runlevels 0 upto 6 and S */
+static int *maxorder; /* Pointer to one of above */
+
+/* See listing.c for list_t and list_entry() macro */
+#define getdir(list) list_entry((list), dir_t, d_list)
+#define getlink(list) list_entry((list), link_t, l_list)
+#define getnextlink(list) (list_empty(list) ? (dir_t*)0 : getlink((list)->next)->target)
+
+/*
+ * We handle services (aka scripts) as directories because
+ * dependencies can be handels as symbolic links therein.
+ * A provided service will be linked into a required service.
+ * For the general type of linked lists see listing.h.
+ */
+
+typedef struct dir_struct dir_t;
+
+typedef struct link_struct {
+ list_t l_list; /* The linked list of symbolic links */
+ dir_t *restrict target;
+} __align link_t; /* This is a "symbolic link" */
+
+typedef struct handle_struct {
+ list_t link; /* The linked list of symbolic start/stop links in the directory */
+ level_t run;
+ ushort flags;
+ uchar mindeep; /* Default start/stop deep if any */
+ uchar deep; /* Current start/stop deep */
+ char * name;
+} __align handle_t;
+
+struct dir_struct {
+ list_t d_list; /* The peg into linked list to other directories */
+ handle_t start;
+ handle_t stopp;
+ service_t *restrict serv;
+ int ref;
+ char * script;
+ char * name;
+} __align; /* This is a "directory" */
+
+#define attof(dir) (&(dir)->serv->attr)
+
+/*
+ * The linked list off all directories, note that the s_list
+ * entry within the dir_struct is used as the peg pointer.
+ */
+static list_t dirs = { &dirs, &dirs };
+static list_t * d_start = &dirs;
+
+#define DIR_SCAN 0x0001
+#define DIR_LOOP 0x0002
+#define DIR_LOOPREPORT 0x0004
+#define DIR_MAXDEEP 0x0008
+#define DIR_SYSTEMD 0x0010
+
+/*
+ * The linked list off all services, note that the d_list
+ * entry within the service_struct is used as the peg pointer.
+ */
+static list_t servs = { &servs, &servs };
+list_t * s_start = &servs;
+
+/*
+ * Provide or find a service dir, set initial states and
+ * link it into the maintaining if a new one.
+ */
+
+static inline dir_t * providedir(const char *restrict const name) attribute((malloc,always_inline,nonnull(1)));
+static inline dir_t * providedir(const char *restrict const name)
+{
+ dir_t *restrict dir = (dir_t*)0;
+ service_t *restrict serv;
+ list_t * ptr;
+
+ list_for_each_prev(ptr, d_start) {
+ dir = getdir(ptr);
+ if (!strcmp(dir->name, name))
+ goto out;
+ }
+
+ if (posix_memalign((void*)&serv, sizeof(void*), alignof(service_t)+strsize(name)) != 0)
+ error("%s", strerror(errno));
+
+ memset(serv, 0, alignof(service_t)+strsize(name));
+ insert(&serv->s_list, s_start->prev);
+ serv->name = ((char*)serv)+alignof(service_t);
+
+ if (posix_memalign((void*)&dir, sizeof(void*), alignof(dir_t)) != 0)
+ error("%s", strerror(errno));
+
+ memset(dir, 0, alignof(dir_t));
+ insert(&dir->d_list, d_start->prev);
+ dir->ref = 1;
+
+ serv->dir = (void*)dir;
+ dir->serv = serv;
+
+ initial(&dir->start.link);
+ initial(&dir->stopp.link);
+
+ initial(&serv->sort.req);
+ initial(&serv->sort.rev);
+
+ strcpy(serv->name, name);
+ dir->name = serv->name;
+ dir->start.name = serv->name;
+ dir->stopp.name = serv->name;
+
+ dir->start.mindeep = 1;
+ dir->stopp.mindeep = 1;
+
+ serv->start = &dir->start.run;
+ serv->stopp = &dir->stopp.run;
+out:
+ return dir;
+}
+
+/*
+ * Find or add and initialize a service
+ */
+service_t * addservice(const char *restrict const serv) attribute((malloc,nonnull(1)));
+service_t * addservice(const char *restrict const serv)
+{
+ service_t * this;
+ list_t * ptr;
+ dir_t * dir;
+
+ list_for_each_prev(ptr, s_start) {
+ this = getservice(ptr);
+ if (!strcmp(this->name, serv))
+ goto out;
+ }
+ dir = providedir(serv);
+ this = dir->serv;
+out:
+ return this;
+}
+
+/*
+ * Always return the address of the original service
+ */
+service_t * getorig(service_t *restrict const serv)
+{
+ dir_t *const dir = (dir_t *)serv->dir;
+ return dir->serv;
+}
+
+/*
+ * Find a service dir by its script name.
+ */
+static inline dir_t * findscript(const char *restrict const script) attribute((always_inline,nonnull(1)));
+static inline dir_t * findscript(const char *restrict const script)
+{
+ dir_t * ret = (dir_t*)0;
+ list_t * ptr;
+
+ list_for_each_prev(ptr, d_start) {
+ dir_t * dir = getdir(ptr);
+
+ if (!dir->script)
+ continue;
+
+ if (!strcmp(dir->script, script)) {
+ ret = dir;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Link the current service into the required service.
+ * If the services do not exist, they will be created.
+ */
+static void ln_sf(dir_t *restrict cur, dir_t *restrict req, const char mode) attribute((nonnull(1,2)));
+static void ln_sf(dir_t *restrict cur, dir_t *restrict req, const char mode)
+{
+ list_t * dent, * l_list = (mode == 'K') ? &req->stopp.link : &req->start.link;
+ link_t *restrict this;
+
+ if (cur == req)
+ goto out;
+
+ list_for_each_prev(dent, l_list) {
+ dir_t * target = getlink(dent)->target;
+ if (!strcmp(target->name, cur->name))
+ goto out;
+ }
+
+ if (posix_memalign((void*)&this, sizeof(void*), alignof(link_t)) == 0) {
+ insert(&this->l_list, l_list->prev);
+ this->target = cur;
+ ++cur->ref;
+ goto out;
+ }
+ error("%s", strerror(errno));
+out:
+ return;
+}
+
+/*
+ * Remember loops to warn only once
+ */
+static inline boolean remembernode (handle_t *restrict const peg) attribute((always_inline,nonnull(1)));
+static inline boolean remembernode (handle_t *restrict const peg)
+{
+ register boolean ret = true;
+
+ if (peg->flags & DIR_LOOP)
+ goto out;
+
+ ret = false;
+ peg->flags |= DIR_LOOP;
+out:
+ return ret;
+}
+
+/*
+ * Recursively called function to follow all
+ * links within a service dir.
+ * Just like a `find * -follow' within a directory tree
+ * of depth one with cross linked dependencies.
+ *
+ * Be warned: the direction is naturally reversed. That
+ * means that the most requested services have the lowest
+ * order. In other word, an empty link list of a service
+ * indicates that this service has a higher order number.
+ */
+#if defined(DEBUG) && (DEBUG > 0)
+# define loop_warn_two(a,b,o) \
+ warn("There is a loop between service %s and %s if %s (list:%d)\n", \
+ (a)->name, (b)->name, o, __LINE__)
+# define loop_warn_one(a,o) \
+ warn("There is a loop at service %s if %s (list:%d)\n", \
+ (a)->name, o, __LINE__)
+#else
+# define loop_warn_two(a,b,o) \
+ warn("There is a loop between service %s and %s if %s\n", (a)->name, (b)->name, o)
+# define loop_warn_one(a,o) \
+ warn("There is a loop at service %s if %s\n", (a)->name, o)
+#endif
+#define loop_check(a) \
+ ((a) && (a)->flags & DIR_LOOP)
+
+static void __follow (dir_t *restrict dir, dir_t *restrict skip, const int, const char, const char)
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+ attribute((noinline,flatten,nonnull(1)));
+#else
+ attribute((noinline,nonnull(1)));
+#endif
+static void __follow (dir_t *restrict dir, dir_t *restrict skip, const int level, const char mode, const char reportloop)
+{
+ list_t * l_list;
+ dir_t * tmp;
+ register int deep = level; /* Link depth, maybe we're called recursively */
+ register int loop = 0; /* Count number of links in symbolic list */
+ handle_t * peg, * pskp = (handle_t*)0;
+ const char * act;
+
+ if (mode == 'K') {
+ peg = &dir->stopp;
+ if (skip) pskp = &skip->stopp;
+ act = "stopped";
+ } else {
+ peg = &dir->start;
+ if (skip) pskp = &skip->start;
+ act = "started";
+ }
+ l_list = &peg->link;
+ prefetch(l_list->next);
+
+ if (peg->flags & DIR_SCAN) {
+ if (pskp) {
+ if (!remembernode(pskp) || !remembernode(peg))
+ loop_warn_two(peg, pskp, act);
+ } else {
+ /* Does this happen? */
+ if (!remembernode(pskp))
+ loop_warn_one(peg, act);
+ }
+ goto out;
+ }
+
+ if (deep < (peg->mindeep)) /* Default deep of this tree is higher */
+ deep = (peg->mindeep);
+
+ if (deep > MAX_DEEP) {
+ if ((peg->flags & DIR_MAXDEEP) == 0)
+ warn("Max recursions depth %d for %s reached\n", MAX_DEEP, peg->name);
+ peg->flags |= DIR_MAXDEEP;
+ goto out;
+ }
+
+ for (tmp = dir; tmp; tmp = getnextlink(l_list)) {
+ const typeof(attof(tmp)->flags) sflags = attof(tmp)->flags;
+ register boolean recursion = true;
+ handle_t * ptmp = (mode == 'K') ? &tmp->stopp : &tmp->start;
+ uchar * order = &ptmp->deep;
+ list_t * dent;
+
+ if (loop++ > MAX_DEEP) {
+ if (pskp) {
+ if (!remembernode(pskp) || !remembernode(ptmp))
+ loop_warn_two(ptmp, pskp, act);
+ } else {
+ if (!remembernode(ptmp))
+ loop_warn_one(ptmp, act);
+ }
+ break; /* Loop detected, stop recursion */
+ }
+ l_list = &ptmp->link; /* List of symbolic links for getnextlink() */
+ prefetch(l_list->next);
+
+ if (!((peg->run.lvl) & (ptmp->run.lvl)))
+ continue; /* Not same boot level */
+
+ if (pskp && pskp == ptmp) {
+ if (!remembernode(pskp) || !remembernode(ptmp))
+ loop_warn_one(pskp, act);
+ break; /* Loop detected, stop recursion */
+ }
+
+ /*
+ * As higher the link depth, as higher the start order.
+ */
+ if (*order > deep)
+ deep = *order;
+ if (*order < deep)
+ *order = deep;
+
+ if ((ptmp->run.lvl) & LVL_ALL) {
+ if (maxorder && (*maxorder < *order))
+ *maxorder = *order;
+ }
+
+ if (list_empty(l_list))
+ break; /* No further service requires this one */
+
+ /*
+ * Do not count the dependcy deep of the system facilities
+ * but follow them to count the replacing provides.
+ */
+
+ if (*ptmp->name == '$')
+ warn("System facilities not fully expanded, see %s!\n", dir->name);
+ else if (++deep > MAX_DEEP) {
+ if ((ptmp->flags & DIR_MAXDEEP) == 0)
+ warn("Max recursions depth %d reached\n", MAX_DEEP);
+ ptmp->flags |= DIR_MAXDEEP;
+ break;
+ }
+
+ ptmp->flags |= DIR_SCAN; /* Mark this service for loop detection */
+
+ /*
+ * If there are links in the links included, follow them
+ */
+ np_list_for_each(dent, l_list) {
+ dir_t * target = getlink(dent)->target;
+ handle_t * ptrg = (mode == 'K') ? &target->stopp : &target->start;
+ const typeof(attof(target)->flags) kflags = attof(target)->flags;
+
+ if ((peg->run.lvl & ptrg->run.lvl) == 0)
+ continue; /* Not same boot level */
+
+ if (target == tmp)
+ break; /* Loop avoided */
+
+ if (target == dir)
+ break; /* Loop avoided */
+
+ if (skip && skip == target) {
+ if (!remembernode(pskp) || !remembernode(ptmp))
+ loop_warn_two(pskp, ptmp, act);
+ recursion = false;
+ break; /* Loop detected, stop recursion */
+ }
+
+ if (mode == 'K') {
+ if (kflags & SERV_FIRST) {
+ warn("Stopping %s depends on %s and therefore on system facility `$all' which can not be true!\n",
+ tmp->script ? tmp->script : tmp->name, target->script ? target->script : target->name);
+ continue;
+ }
+ } else {
+ if (sflags & SERV_ALL) {
+ warn("Starting %s depends on %s and therefore on system facility `$all' which can not be true!\n",
+ target->script ? target->script : target->name, tmp->script ? tmp->script : tmp->name);
+ continue;
+ }
+ }
+
+ if (ptrg->deep >= deep) /* Nothing new */
+ continue;
+ /* The inner recursion */
+ __follow(target, tmp, deep, mode, reportloop);
+ prefetch(dent->next);
+
+ /* Just for the case an inner recursion was stopped */
+ if (loop_check(ptrg) || loop_check(ptmp) || loop_check(pskp)) {
+ recursion = false;
+ break; /* Loop detected, stop recursion */
+ }
+ }
+
+ ptmp->flags &= ~DIR_SCAN; /* Remove loop detection mark */
+ prefetch(l_list->next);
+
+ if (!recursion) {
+ if (reportloop && !(ptmp->flags & DIR_LOOPREPORT)) {
+ warn(" loop involving service %s at depth %d\n", tmp->name, level);
+ ptmp->flags |= DIR_LOOPREPORT;
+ }
+ break; /* Loop detected, stop recursion */
+ }
+ }
+out:
+ return; /* Make compiler happy */
+}
+
+#undef loop_warn_two
+#undef loop_warn_one
+#undef loop_check
+
+/*
+ * Helper for follow_all: start with depth one.
+ */
+static inline void follow(dir_t *restrict dir, const char mode, const char reportloop) attribute((always_inline,nonnull(1)));
+static inline void follow(dir_t *restrict dir, const char mode, const char reportloop)
+{
+ const int deep = (mode == 'K') ? (dir->stopp.mindeep) : (dir->start.mindeep);
+ /* Link depth starts here with one */
+ __follow(dir, (dir_t*)0, deep, mode, reportloop);
+}
+
+/*
+ * Put not existing services into a guessed order.
+ * The maximal order of not existing services can be
+ * set if they are required by existing services.
+ */
+static void guess_order(dir_t *restrict dir, const char mode) attribute((nonnull(1)));
+static void guess_order(dir_t *restrict dir, const char mode)
+{
+ handle_t * peg = (mode == 'K') ? &dir->stopp : &dir->start;
+ list_t * l_list = &peg->link;
+ register int min = 99;
+ register int deep = 0;
+ ushort lvl = 0;
+
+ if (dir->script) /* Skip it because we have read it */
+ goto out;
+
+ if (*dir->name == '$') { /* Don't touch our system facilities */
+ warn("System facilities not fully expanded, see %s!\n", dir->name);
+ goto out;
+ }
+
+ /* No full loop required because we seek for the lowest order */
+ if (!list_empty(l_list)) {
+ dir_t * target = getnextlink(l_list);
+ handle_t * ptrg = (mode == 'K') ? &target->stopp : &target->start;
+ uchar * order = &ptrg->deep;
+ list_t * dent;
+
+ if ( (order) && (min > *order) ) /* add check to avoid null pointer */
+ min = *order;
+
+ lvl |= ptrg->run.lvl;
+
+ list_for_each_prev(dent, l_list) {
+ dir_t * tmp = getlink(dent)->target;
+ handle_t * ptmp = (mode == 'K') ? &tmp->stopp : &tmp->start;
+ uchar * order = &ptmp->deep;
+
+ if (++deep > MAX_DEEP)
+ break;
+
+ if (target == dir)
+ break; /* Loop detected */
+
+ if (min > *order)
+ min = *order;
+
+ lvl |= ptmp->run.lvl;
+ }
+ if (min > 1) { /* Set guessed order of this unknown script */
+ uchar * order = &peg->deep;
+ *order = min - 1;
+ peg->run.lvl |= lvl; /* Set guessed runlevels of this unknown script */
+ } else {
+ peg->run.lvl = LVL_BOOT;
+ }
+ }
+out:
+ return;
+}
+
+/*
+ * Sort linked list of provides into start or stop order
+ * during this set new start or stop order of the serives.
+ */
+#define getdep(req) ((dir_t*)(req)->serv->dir)
+void lsort(const char type)
+{
+ list_t sort = { &sort, &sort };
+ list_t * ptr, * safe, * this;
+ int order;
+
+ switch (type) {
+ case 'K':
+ for (order = 0; order <= maxstop; order++) {
+ list_for_each_safe(ptr, safe, d_start) {
+ dir_t * dir = getdir(ptr);
+ if (dir->stopp.deep == order)
+ move_tail(ptr, &sort);
+ }
+ }
+ join(&sort, d_start);
+ list_for_each(this, s_start) {
+ service_t * serv = getservice(this);
+ if (serv->attr.flags & SERV_DUPLET)
+ continue;
+ initial(&sort);
+ for (order = maxstop; order >= 0; order--) {
+ list_for_each_safe(ptr, safe, &serv->sort.rev) {
+ req_t * rev = getreq(ptr);
+ dir_t * dir = getdep(rev);
+ if (dir->stopp.deep == order) {
+ service_t *const orig = getorig(rev->serv);
+ list_t * chk;
+ boolean found = false;
+
+ list_for_each_prev(chk, &sort) { /* check if service was already resorted */
+ req_t * this = getreq(chk);
+ if (getdep(this)->stopp.deep != order)
+ break; /* added on tail always with same order */
+ if (getdep(this) == orig->dir) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ if (rev->serv != orig) { /* replace alias with its original */
+ req_t *restrict this;
+ if (posix_memalign((void*)&this, sizeof(void*), alignof(req_t)) != 0)
+ error("%s", strerror(errno));
+ memset(this, 0, alignof(req_t));
+ this->flags = rev->flags;
+ this->serv = orig;
+ replace(ptr, &this->list);
+ ptr = &this->list;
+ free(rev);
+ }
+ move_tail(ptr, &sort);
+ } else { /* already included */
+ delete(ptr);
+ free(rev);
+ }
+ }
+ }
+ }
+ join(&sort, &serv->sort.rev);
+ }
+ break;
+ default:
+ for (order = 0; order <= maxstart; order++) {
+ list_for_each_safe(ptr, safe, d_start) {
+ dir_t * dir = getdir(ptr);
+ if (dir->start.deep == order)
+ move_tail(ptr, &sort);
+ }
+ }
+ join(&sort, d_start);
+ list_for_each(this, s_start) {
+ service_t * serv = getservice(this);
+ if (serv->attr.flags & SERV_DUPLET)
+ continue;
+ initial(&sort);
+ for (order = maxstart; order >= 0; order--) {
+ list_for_each_safe(ptr, safe, &serv->sort.req) {
+ req_t * req = getreq(ptr);
+ dir_t * dir = getdep(req);
+ if (dir->start.deep == order) {
+ service_t * orig = getorig(req->serv);
+ list_t * chk;
+ boolean found = false;
+
+ list_for_each_prev(chk, &sort) { /* check if service was already resorted */
+ req_t * this = getreq(chk);
+ if (getdep(this)->start.deep != order)
+ break; /* added on tail always with same order */
+ if (getdep(this) == orig->dir) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (req->serv != orig) { /* replace alias with its original */
+ req_t *restrict this;
+ if (posix_memalign((void*)&this, sizeof(void*), alignof(req_t)) != 0)
+ error("%s", strerror(errno));
+ memset(this, 0, alignof(req_t));
+ this->flags = req->flags;
+ this->serv = orig;
+ replace(ptr, &this->list);
+ ptr = &this->list;
+ free(req);
+ }
+ move_tail(ptr, &sort);
+ } else { /* already included */
+ delete(ptr);
+ free(req);
+ }
+ }
+ }
+ }
+ join(&sort, &serv->sort.req);
+ }
+ break;
+ }
+}
+
+/*
+ * Clear out aliases of existing services, that is that for *one* script there
+ * exist several provides which could have have been required different by
+ * other services. This avoids doing the same work several times.
+ */
+void nickservice(service_t *restrict orig, service_t *restrict nick)
+{
+ dir_t * dir = (dir_t*)orig->dir;
+ dir_t * cmp = (dir_t*)nick->dir;
+ list_t * dent, * safe;
+
+ if (dir == cmp)
+ return;
+
+ if (cmp->script && cmp->script != dir->script)
+ return;
+
+ list_for_each_safe(dent, safe, &cmp->start.link) {
+ link_t * link = getlink(dent);
+ dir_t * target = link->target;
+
+ if (target == cmp)
+ continue;
+
+ ln_sf(target, dir, 'S');
+
+ /* remove the link from local link list but never free the target */
+
+ delete(dent);
+ free(link);
+ }
+
+ list_for_each_safe(dent, safe, &cmp->stopp.link) {
+ link_t * link = getlink(dent);
+ dir_t * target = link->target;
+
+ if (target == cmp)
+ continue;
+
+ ln_sf(target, dir, 'K');
+
+ /* remove the link from local link list but never free the target */
+
+ delete(dent);
+ free(link);
+ }
+
+ delete(&cmp->d_list); /* remove alias entry from global service list */
+
+ /* remember levels of old start handle */
+ dir->start.run.lvl |= cmp->start.run.lvl;
+ dir->start.flags |= cmp->start.flags;
+
+ /* remember levels of old stop handle */
+ dir->stopp.run.lvl |= cmp->stopp.run.lvl;
+ dir->stopp.flags |= cmp->stopp.flags;
+
+ /* remember global flags of old provide */
+ orig->attr.flags |= nick->attr.flags;
+ nick->attr.flags |= SERV_DUPLET;
+
+ if (cmp->script && cmp->script != dir->script) {
+ free(nick->attr.script);
+ nick->attr.script = orig->attr.script;
+ }
+
+ nick->dir = (void*)dir; /* remember main provide */
+ nick->start = &dir->start.run;
+ nick->stopp = &dir->stopp.run;
+
+ if (--cmp->ref <= 0) free(cmp);
+
+ list_for_each_safe(dent, safe, &nick->sort.req) {
+ req_t * this = getreq(dent);
+ boolean ok = true;
+ list_t * req;
+ list_for_each(req, &orig->sort.req) {
+ if (!strcmp(this->serv->name,getreq(req)->serv->name)) {
+ ok = false;
+ break;
+ }
+ }
+ if (!ok) {
+ delete(dent);
+ free(this);
+ } else
+ move_tail(dent, &orig->sort.req);
+ }
+
+ list_for_each_safe(dent, safe, &nick->sort.rev) {
+ req_t * this = getreq(dent);
+ boolean ok = true;
+ list_t * rev;
+ list_for_each(rev, &orig->sort.rev) {
+ if (!strcmp(this->serv->name,getreq(rev)->serv->name)) {
+ ok = false;
+ break;
+ }
+ }
+ if (!ok) {
+ delete(dent);
+ free(this);
+ } else
+ move_tail(dent, &orig->sort.rev);
+ }
+}
+
+void clear_all(void)
+{
+ list_t * this;
+
+ /*
+ * Find dangling links in global service list and remove them
+ * if we by detect the remove bit from set above in the flags.
+ */
+
+ list_for_each(this, d_start) {
+ dir_t *dir = getdir(this);
+ list_t *dent, *hold;
+
+ list_for_each_safe(dent, hold, &dir->start.link) {
+ link_t * link = getlink(dent);
+ dir_t * target = link->target;
+
+ if (target == dir)
+ continue;
+
+ if ((attof(target)->flags & SERV_DUPLET) == 0)
+ continue;
+
+ /* remove the link from local link list */
+
+ delete(dent);
+ free(link);
+
+ /*
+ * Do not free allocated strings and structure if in use
+ * never free cmp->attr.script as this remains always in use.
+ */
+
+ if (--target->ref <= 0) free(target);
+ }
+
+ list_for_each_safe(dent, hold, &dir->stopp.link) {
+ link_t * link = getlink(dent);
+ dir_t * target = link->target;
+
+ if (target == dir)
+ continue;
+
+ if ((attof(target)->flags & SERV_DUPLET) == 0)
+ continue;
+
+ /* remove the link from local link list */
+
+ delete(dent);
+ free(link);
+
+ /*
+ * Do not free allocated strings and structure if in use
+ * never free cmp->attr.script as this remains always in use.
+ */
+
+ if (--target->ref <= 0) free(target);
+ }
+ }
+#if defined(DEBUG) && (DEBUG > 0)
+ list_for_each(this, s_start) {
+ service_t * srv = getservice(this);
+ list_t * nxt, * hold;
+
+ if (srv->attr.flags & SERV_DUPLET)
+ continue;
+
+ list_for_each_safe(nxt, hold, s_start) {
+ list_t * dent, * safe;
+ service_t * orv;
+
+ orv = getservice(nxt);
+
+ if ((orv->attr.flags & SERV_DUPLET) == 0)
+ continue;
+
+ if (srv->dir != orv->dir)
+ continue;
+
+ srv->attr.flags |= orv->attr.flags;
+ srv->attr.flags &= ~SERV_DUPLET;
+
+ list_for_each_safe(dent, safe, &orv->sort.req) {
+ req_t * this = getreq(dent);
+ boolean ok = true;
+ list_t * req;
+ list_for_each(req, &srv->sort.req) {
+ if (!strcmp(this->serv->name,getreq(req)->serv->name)) {
+ ok = false;
+ break;
+ }
+ }
+ if (!ok) {
+ fprintf(stderr, "BUG: removed %s from start list of %s, missed getorig()?\n",
+ this->serv->name, orv->name);
+ delete(dent);
+ free(this);
+ } else {
+ fprintf(stderr, "BUG: moved %s from start list of %s to %s, missed getorig()?\n",
+ this->serv->name, orv->name, srv->name);
+ move_tail(dent, &srv->sort.req);
+ }
+ }
+
+ list_for_each_safe(dent, safe, &orv->sort.rev) {
+ req_t * this = getreq(dent);
+ boolean ok = true;
+ list_t * rev;
+ list_for_each(rev, &srv->sort.rev) {
+ if (!strcmp(this->serv->name,getreq(rev)->serv->name)) {
+ ok = false;
+ break;
+ }
+ }
+ if (!ok) {
+ fprintf(stderr, "BUG: removed %s from start list of %s, missed getorig()?\n",
+ this->serv->name, orv->name);
+ delete(dent);
+ free(this);
+ } else {
+ fprintf(stderr, "BUG: moved %s from start list of %s to %s, missed getorig()?\n",
+ this->serv->name, orv->name, srv->name);
+ move_tail(dent, &srv->sort.rev);
+ }
+ }
+ }
+ }
+#endif
+}
+
+/*
+ * Follow all services and their dependencies recursivly.
+ */
+void follow_all(void)
+{
+ list_t *tmp;
+
+ /*
+ * Follow all scripts and calculate the main ordering.
+ */
+ list_for_each(tmp, d_start) {
+ maxorder = &maxstart;
+ follow(getdir(tmp), 'S', 1);
+ maxorder = &maxstop;
+ follow(getdir(tmp), 'K', 1);
+ }
+
+ /*
+ * Guess order of not installed scripts in comparision
+ * to the well known scripts.
+ */
+ list_for_each(tmp, d_start) {
+ maxorder = &maxstart;
+ guess_order(getdir(tmp), 'S');
+ maxorder = &maxstop;
+ guess_order(getdir(tmp), 'K');
+ }
+}
+
+boolean is_loop_detected(void)
+{
+ list_t *tmp;
+ list_for_each(tmp, d_start) {
+ dir_t * dir = getdir(tmp);
+ if (dir->start.flags & DIR_LOOPREPORT)
+ return true;
+ if (dir->stopp.flags & DIR_LOOPREPORT)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * For debuging: show all services
+ */
+void show_all()
+{
+ list_t *tmp;
+ if (maxstop > 0) list_for_each(tmp, d_start) {
+ char * script, *lvlstr;
+#if defined(DEBUG) && (DEBUG > 0)
+ char *name;
+#endif
+ dir_t * dir = getdir(tmp);
+ handle_t * peg;
+ uchar deep;
+ ushort lvl;
+ if (!dir)
+ continue;
+#if defined(DEBUG) && (DEBUG > 0)
+ name = dir->name;
+#endif
+ peg = &dir->stopp;
+ lvl = peg->run.lvl;
+ lvlstr = lvl2str(lvl);
+ deep = peg->deep;
+ if (attof(dir)->script)
+ script = attof(dir)->script;
+#if defined(DEBUG) && (DEBUG > 0)
+ else if (*name == '$')
+ script = "%system";
+ else
+ script = "%guessed";
+ info(1, "K%.2d %s 0x%.2x '%s' (%s)\n", deep, name, lvl, lvlstr,
+ script);
+#else
+ else
+ script = NULL;
+ if (script && lvlstr)
+ fprintf(stdout, "K:%.2d:%s:%s\n", deep, lvlstr, script);
+#endif
+ xreset(lvlstr);
+ }
+ if (maxstart > 0) list_for_each(tmp, d_start) {
+ char * script, *lvlstr;
+#if defined(DEBUG) && (DEBUG > 0)
+ char *name;
+#endif
+ dir_t * dir = getdir(tmp);
+ handle_t * peg;
+ uchar deep;
+ ushort lvl;
+ if (!dir)
+ continue;
+#if defined(DEBUG) && (DEBUG > 0)
+ name = dir->name;
+#endif
+ peg = &dir->start;
+ lvl = peg->run.lvl;
+ lvlstr = lvl2str(lvl);
+ deep = peg->deep;
+ if (attof(dir)->script)
+ script = attof(dir)->script;
+#if defined(DEBUG) && (DEBUG > 0)
+ else if (*name == '$')
+ script = "%system";
+ else
+ script = "%guessed";
+ info(1, "S%.2d %s 0x%.2x '%s' (%s)\n", deep, name, lvl, lvlstr,
+ script);
+#else
+ else
+ script = NULL;
+ if (script && lvlstr)
+ fprintf(stdout, "S:%.2d:%s:%s\n", deep, lvlstr, script);
+#endif
+ xreset(lvlstr);
+ }
+}
+
+/*
+ * Used within loops to get scripts not included in this runlevel
+ */
+boolean notincluded(const char *restrict const script, const char mode, const int runlevel)
+{
+ list_t *tmp;
+ boolean ret = false;
+ const ushort lvl = map_runlevel_to_lvl (runlevel);
+
+ list_for_each_prev(tmp, d_start) {
+ dir_t * dir = getdir(tmp);
+ level_t * run = (mode == 'K') ? &dir->stopp.run : &dir->start.run;
+
+ if (run->lvl & lvl) /* Same runlevel */
+ continue;
+
+ if (dir->script == (char*)0) /* No such file */
+ continue;
+
+ if (strcmp(script, dir->script))
+ continue; /* Not this file */
+
+ ret = true; /* Not included */
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Used within loops to list services an for a given runlevel bit mask.
+ */
+service_t * listscripts(const char **restrict script, const char mode, const ushort lvl)
+{
+ static list_t * tmp;
+ service_t * serv;
+ ushort level;
+ dir_t * dir;
+
+ if (!*script)
+ tmp = d_start->next;
+
+ do {
+ serv = (service_t*)0;
+ if (tmp == d_start)
+ break;
+ if (tmp->next)
+ prefetch(tmp->next);
+ dir = getdir(tmp);
+
+ attof(dir)->korder = dir->stopp.deep;
+ attof(dir)->sorder = dir->start.deep;
+
+ serv = dir->serv;
+ *script = serv->attr.script;
+
+ switch (mode) {
+ case 'X':
+ level = (dir->stopp.run.lvl|dir->start.run.lvl);
+ break;
+ case 'K':
+ level = dir->stopp.run.lvl;
+ break;
+ default:
+ level = dir->start.run.lvl;
+ break;
+ }
+
+ tmp = tmp->next;
+
+ } while ((*script == (char*)0) || (level & lvl) == 0);
+
+ return serv;
+}
+
+/*
+ * THIS services DEPENDS on that service befor startup or shutdown.
+ */
+void requires(service_t *restrict this, service_t *restrict dep, const char mode)
+{
+ ln_sf((dir_t*)this->dir, (dir_t*)dep->dir, mode);
+ if (this->attr.flags & SERV_SYSTEMD) {
+ dir_t *dir = (dir_t*)this->dir;
+ handle_t *peg = &dir->stopp;
+ peg->flags |= DIR_SYSTEMD;
+ peg = &dir->start;
+ peg->flags |= DIR_SYSTEMD;
+ }
+ if (dep->attr.flags & SERV_SYSTEMD) {
+ dir_t *dir = (dir_t*)dep->dir;
+ handle_t *peg = &dir->stopp;
+ peg->flags |= DIR_SYSTEMD;
+ peg = &dir->start;
+ peg->flags |= DIR_SYSTEMD;
+ }
+}
+
+/*
+ * Set the runlevels of a service.
+ */
+void runlevels(service_t *restrict serv, const char mode, const char *restrict lvl)
+{
+ dir_t *dir = (dir_t *)serv->dir;
+ handle_t * peg = (mode == 'K') ? &dir->stopp : &dir->start;
+ peg->run.lvl |= str2lvl(lvl);
+}
+
+/*
+ * Reorder all services starting with a service
+ * being in same runlevels.
+ */
+void setorder(const char *restrict script, const char mode, const int order, const boolean recursive)
+{
+ dir_t * dir = findscript(script);
+ handle_t * peg;
+ list_t * tmp;
+
+ if (!dir)
+ goto out;
+
+ if (mode == 'K') {
+ peg = &dir->stopp;
+ maxorder = &maxstop;
+ } else {
+ peg = &dir->start;
+ maxorder = &maxstart;
+ }
+
+ if (peg->mindeep < order)
+ peg->mindeep = order; /* Remember lowest default order deep */
+
+ if (peg->deep >= peg->mindeep) /* Nothing to do */
+ goto out;
+
+ if (!recursive) {
+ peg->deep = peg->mindeep;
+ goto out;
+ }
+
+ /*
+ * Follow the script and re-calculate the ordering.
+ */
+ __follow(dir, (dir_t*)0, peg->mindeep, mode, 0);
+
+ /*
+ * Guess order of not installed scripts in comparision
+ * to the well known scripts.
+ */
+ list_for_each(tmp, d_start)
+ guess_order(getdir(tmp), mode);
+out:
+ return;
+}
+
+/*
+ * Get the order of a script.
+ */
+int getorder(const char *restrict script, const char mode)
+{
+ dir_t * dir = findscript(script);
+ int order = 0;
+
+ if (dir) {
+ handle_t * peg = (mode == 'K') ? &dir->stopp : &dir->start;
+ order = peg->deep;
+ }
+
+ return order;
+}
+
+/*
+ * Provide a service if the corresponding script
+ * was read and the scripts name was remembered.
+ * A given script name marks a service as a readed one.
+ * One script and several provided facilities leads
+ * to the same order for those facilities.
+ */
+boolean makeprov(service_t *restrict serv, const char *restrict script)
+{
+ dir_t *restrict alias = findscript(script);
+ dir_t *restrict dir = (dir_t *restrict)serv->dir;
+ boolean ret = true;
+
+ if (!dir->script) {
+ list_t * ptr;
+ if (!alias) {
+ serv->attr.script = xstrdup(script);
+ serv->attr.flags |= SERV_SCRIPT;
+ dir->script = serv->attr.script;
+ } else
+ dir->script = alias->script;
+
+ list_for_each(ptr, s_start) {
+ service_t * tmp = getservice(ptr);
+ if (tmp == serv)
+ continue;
+ if (tmp->dir != serv->dir)
+ continue;
+ if (tmp->attr.script)
+ continue;
+ tmp->attr.script = dir->script;
+ tmp->attr.flags |= SERV_SCRIPT;
+ }
+
+ } else if (strcmp(dir->script, script))
+ ret = false;
+
+ return ret;
+}
+
+/*
+ * Find the script name of a provided feature
+ */
+const char * getscript(const char *restrict prov)
+{
+ char * script = (char*)0;
+ list_t * ptr;
+
+ list_for_each(ptr, s_start) {
+ service_t * this = getservice(ptr);
+ if (!strcmp(this->name, prov)) {
+ if (this->attr.script)
+ script = this->attr.script;
+ break;
+ }
+ }
+ return script;
+}
+
+/*
+ * Return the provided service of a given script
+ */
+const char * getprovides(const char *restrict script)
+{
+ const dir_t * dir = findscript(script);
+ const char * prov = (const char*)0;
+
+ if (dir)
+ prov = dir->name;
+ return prov;
+}
+
+/*
+ * Find a specific service by its name
+ */
+service_t * findservice(const char *restrict const name)
+{
+ list_t * ptr;
+ service_t * ret = (service_t*)0;
+
+ if (name == (const char*)0)
+ goto out;
+
+ list_for_each(ptr, s_start) {
+ service_t * this = getservice(ptr);
+ if (!strcmp(this->name, name)) {
+ ret = this;
+ break;
+ }
+ }
+out:
+ return ret;
+}