/* * 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 #include #include #include #include #include #include #include #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; }