insserv/listing.c

1283 lines
32 KiB
C

/*
* 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;
int impossible = 0;
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;
impossible = 1;
}
}
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 ( (impossible || 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;
}