diff options
author | Dmitry Bogatov <KAction@debian.org> | 2019-09-28 01:02:45 +0100 |
---|---|---|
committer | Dmitry Bogatov <KAction@debian.org> | 2019-09-28 01:02:45 +0100 |
commit | 4d8459ef873ca4f90ded1a6bab58a53f0ee87e6c (patch) | |
tree | 321432239f40aaa31a5a4dd0add04b434bd54484 /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.c | 1279 |
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; +} |