migration from private to rsa
[external/insserv.git] / listing.c
1 /*
2  * listing.c
3  *
4  * Copyright 2000-2008 Werner Fink, 2000 SuSE GmbH Nuernberg, Germany,
5  *                                  2003 SuSE Linux AG, Germany.
6  *                             2007-2008 SuSE Linux Products GmbH Nuernberg, Germany
7  *
8  * This source is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  */
13
14 #include <string.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <errno.h>
19 #include <limits.h>
20 #include <sys/types.h>
21 #include <ctype.h>
22 #include "listing.h"
23
24 int maxstart = 0;               /* Maximum start order of runlevels 0 upto 6 and S */
25 int maxstop  = 0;               /* Maximum stop  order of runlevels 0 upto 6 and S */
26 static int *maxorder;           /* Pointer to one of above */
27
28 /* See listing.c for list_t and list_entry() macro */
29 #define getdir(list)            list_entry((list), dir_t,   d_list)
30 #define getlink(list)           list_entry((list), link_t,  l_list)
31 #define getnextlink(list)       (list_empty(list) ? (dir_t*)0 : getlink((list)->next)->target)
32
33 /*
34  * We handle services (aka scripts) as directories because
35  * dependencies can be handels as symbolic links therein.
36  * A provided service will be linked into a required service.
37  * For the general type of linked lists see listing.h.
38  */
39
40 typedef struct dir_struct dir_t;
41
42 typedef struct link_struct {
43     list_t                l_list;       /* The linked list of symbolic links */
44     dir_t       *restrict target;
45 } __align link_t;                       /* This is a "symbolic link" */
46
47 typedef struct handle_struct {
48     list_t                  link;       /* The linked list of symbolic start/stop links in the directory */
49     level_t                  run;
50     ushort                 flags;
51     uchar                mindeep;       /* Default start/stop deep if any */
52     uchar                   deep;       /* Current start/stop deep */
53     char                  * name;
54 } __align handle_t;
55
56 struct dir_struct {
57     list_t                d_list;       /* The peg into linked list to other directories */
58     handle_t               start;
59     handle_t               stopp;
60     service_t     *restrict serv;
61     int                      ref;
62     char                * script;
63     char                  * name;
64 } __align;                              /* This is a "directory" */
65
66 #define attof(dir)      (&(dir)->serv->attr)
67
68 /*
69  * The linked list off all directories, note that the s_list
70  * entry within the dir_struct is used as the peg pointer.
71  */
72 static list_t dirs = { &dirs, &dirs };
73 static list_t * d_start = &dirs;
74
75 #define DIR_SCAN        0x0001
76 #define DIR_LOOP        0x0002
77 #define DIR_LOOPREPORT  0x0004
78 #define DIR_MAXDEEP     0x0008
79
80 /*
81  * The linked list off all services, note that the d_list
82  * entry within the service_struct is used as the peg pointer.
83  */
84 static list_t servs = { &servs, &servs };
85 list_t * s_start = &servs;
86
87 /*
88  * Provide or find a service dir, set initial states and
89  * link it into the maintaining if a new one.
90  */
91
92 static inline dir_t * providedir(const char *restrict const name) attribute((malloc,always_inline,nonnull(1)));
93 static inline dir_t * providedir(const char *restrict const name)
94 {
95     dir_t *restrict dir = (dir_t*)0;
96     service_t *restrict serv;
97     list_t * ptr;
98
99     list_for_each_prev(ptr, d_start) {
100         dir = getdir(ptr);
101         if (!strcmp(dir->name, name))
102             goto out;
103     }
104
105     if (posix_memalign((void*)&serv, sizeof(void*), alignof(service_t)+strsize(name)) != 0)
106         error("%s", strerror(errno));
107
108     memset(serv, 0, alignof(service_t)+strsize(name));
109     insert(&serv->s_list, s_start->prev);
110     serv->name = ((char*)serv)+alignof(service_t);
111
112     if (posix_memalign((void*)&dir, sizeof(void*), alignof(dir_t)) != 0)
113         error("%s", strerror(errno));
114
115     memset(dir, 0, alignof(dir_t));
116     insert(&dir->d_list, d_start->prev);
117     dir->ref = 1;
118
119     serv->dir = (void*)dir;
120     dir->serv = serv;
121
122     initial(&dir->start.link);
123     initial(&dir->stopp.link);
124
125     initial(&serv->sort.req);
126     initial(&serv->sort.rev);
127
128     strcpy(serv->name, name);
129     dir->name       = serv->name;
130     dir->start.name = serv->name;
131     dir->stopp.name = serv->name;
132
133     dir->start.mindeep = 1;
134     dir->stopp.mindeep = 1;
135
136     serv->start = &dir->start.run;
137     serv->stopp = &dir->stopp.run;
138 out:
139     return dir;
140 }
141
142 /*
143  * Find or add and initialize a service
144  */
145 service_t * addservice(const char *restrict const serv) attribute((malloc,nonnull(1)));
146 service_t * addservice(const char *restrict const serv)
147 {
148     service_t * this;
149     list_t * ptr;
150     dir_t * dir;
151
152     list_for_each_prev(ptr, s_start) {
153         this = getservice(ptr);
154         if (!strcmp(this->name, serv))
155             goto out;
156     }
157     dir = providedir(serv);
158     this = dir->serv;
159 out:
160     return this;
161 }
162
163 /*
164  * Always return the address of the original service
165  */
166 service_t * getorig(service_t *restrict const serv)
167 {
168     dir_t *const dir = (dir_t *)serv->dir;
169     return dir->serv;
170 }
171
172 /*
173  * Find a service dir by its script name.
174  */
175 static inline dir_t * findscript(const char *restrict const script) attribute((always_inline,nonnull(1)));
176 static inline dir_t * findscript(const char *restrict const script)
177 {
178     dir_t  * ret = (dir_t*)0;
179     list_t * ptr;
180
181     list_for_each_prev(ptr, d_start) {
182         dir_t * dir = getdir(ptr);
183
184         if (!dir->script)
185             continue;
186
187         if (!strcmp(dir->script, script)) {
188             ret = dir;
189             break;
190         }
191     }
192
193     return ret;
194 }
195
196 /*
197  * Link the current service into the required service.
198  * If the services do not exist, they will be created.
199  */
200 static void ln_sf(dir_t *restrict cur, dir_t *restrict req, const char mode) attribute((nonnull(1,2)));
201 static void ln_sf(dir_t *restrict cur, dir_t *restrict req, const char mode)
202 {
203     list_t * dent, * l_list = (mode == 'K') ? &req->stopp.link : &req->start.link;
204     link_t *restrict this;
205
206     if (cur == req)
207         goto out;
208
209     list_for_each_prev(dent, l_list) {
210         dir_t * target = getlink(dent)->target;
211         if (!strcmp(target->name, cur->name))
212             goto out;
213     }
214
215     if (posix_memalign((void*)&this, sizeof(void*), alignof(link_t)) == 0) {
216         insert(&this->l_list, l_list->prev);
217         this->target = cur;
218         ++cur->ref;
219         goto out;
220     }
221     error("%s", strerror(errno));
222 out:
223     return;
224 }
225
226 /*
227  * Remember loops to warn only once
228  */
229 static inline boolean remembernode (handle_t *restrict const peg) attribute((always_inline,nonnull(1)));
230 static inline boolean remembernode (handle_t *restrict const peg)
231 {
232     register boolean ret = true;
233
234     if (peg->flags & DIR_LOOP)
235         goto out;
236
237     ret = false;
238     peg->flags |= DIR_LOOP;
239 out:
240     return ret;
241 }
242
243 /*
244  * Recursively called function to follow all
245  * links within a service dir.
246  * Just like a `find * -follow' within a directory tree
247  * of depth one with cross linked dependencies.
248  *
249  * Be warned: the direction is naturally reversed.  That
250  * means that the most requested services have the lowest
251  * order.  In other word, an empty link list of a service
252  * indicates that this service has a higher order number.
253  */
254 #if defined(DEBUG) && (DEBUG > 0)
255 # define loop_warn_two(a,b,o)   \
256         warn("There is a loop between service %s and %s if %s (list:%d)\n", \
257         (a)->name, (b)->name, o, __LINE__)
258 # define loop_warn_one(a,o)     \
259         warn("There is a loop at service %s if %s (list:%d)\n", \
260         (a)->name, o, __LINE__)
261 #else
262 # define loop_warn_two(a,b,o)   \
263         warn("There is a loop between service %s and %s if %s\n", (a)->name, (b)->name, o)
264 # define loop_warn_one(a,o)     \
265         warn("There is a loop at service %s if %s\n", (a)->name, o)
266 #endif
267 #define loop_check(a)   \
268         ((a) && (a)->flags & DIR_LOOP)
269
270 static void __follow (dir_t *restrict dir, dir_t *restrict skip, const int, const char, const char)
271 #if  __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
272         attribute((noinline,flatten,nonnull(1)));
273 #else
274         attribute((noinline,nonnull(1)));
275 #endif
276 static void __follow (dir_t *restrict dir, dir_t *restrict skip, const int level, const char mode, const char reportloop)
277 {
278     list_t * l_list;
279     dir_t * tmp;
280     register int deep = level;  /* Link depth, maybe we're called recursively */
281     register int loop = 0;      /* Count number of links in symbolic list */
282     handle_t * peg, * pskp = (handle_t*)0;
283     const char * act;
284
285     if (mode == 'K') {
286         peg = &dir->stopp;
287         if (skip) pskp = &skip->stopp;
288         act  = "stopped";
289     } else {
290         peg = &dir->start;
291         if (skip) pskp = &skip->start;
292         act  = "started";
293     }
294     l_list = &peg->link;
295     prefetch(l_list->next);
296
297     if (peg->flags & DIR_SCAN) {
298         if (pskp) {
299             if (!remembernode(pskp) || !remembernode(peg))
300                 loop_warn_two(peg, pskp, act);
301         } else {
302             /* Does this happen? */
303             if (!remembernode(pskp))
304                 loop_warn_one(peg, act);
305         }
306         goto out;
307     }
308
309     if (deep < (peg->mindeep))  /* Default deep of this tree is higher */
310         deep = (peg->mindeep);
311
312     if (deep > MAX_DEEP) {
313         if ((peg->flags & DIR_MAXDEEP) == 0)
314             warn("Max recursions depth %d for %s reached\n",  MAX_DEEP, peg->name);
315         peg->flags |= DIR_MAXDEEP;
316         goto out;
317     }
318
319     for (tmp = dir; tmp; tmp = getnextlink(l_list)) {
320         register boolean recursion = true;
321         handle_t * ptmp = (mode == 'K') ? &tmp->stopp : &tmp->start;
322         uchar  * order = &ptmp->deep;
323         list_t * dent;
324
325         if (loop++ > MAX_DEEP) {
326             if (pskp) {
327                 if (!remembernode(pskp) || !remembernode(ptmp))
328                     loop_warn_two(ptmp, pskp, act);
329             } else {
330                 if (!remembernode(ptmp))
331                     loop_warn_one(ptmp, act);
332             }
333             break;                      /* Loop detected, stop recursion */
334         }
335         l_list = &ptmp->link;           /* List of symbolic links for getnextlink() */
336         prefetch(l_list->next);
337
338         if (!((peg->run.lvl) & (ptmp->run.lvl)))
339              continue;                  /* Not same boot level */
340
341         if (pskp && pskp == ptmp) {
342             if (!remembernode(pskp) || !remembernode(ptmp))
343                 loop_warn_one(pskp, act);
344             break;                      /* Loop detected, stop recursion */
345         }
346
347         /*
348          * As higher the link depth, as higher the start order.
349          */
350         if (*order > deep)
351             deep = *order;
352         if (*order < deep)
353             *order = deep;
354
355         if ((ptmp->run.lvl) & LVL_ALL) {
356             if (maxorder && (*maxorder < *order))
357                 *maxorder = *order;
358         }
359
360         if (list_empty(l_list))
361             break;                      /* No further service requires this one */
362
363         /*
364          * Do not count the dependcy deep of the system facilities
365          * but follow them to count the replacing provides.
366          */
367         if (*ptmp->name == '$')
368             warn("System facilities not fully expanded, see %s!\n", dir->name);
369         else if (++deep > MAX_DEEP) {
370             if ((ptmp->flags & DIR_MAXDEEP) == 0)
371                 warn("Max recursions depth %d reached\n",  MAX_DEEP);
372             ptmp->flags |= DIR_MAXDEEP;
373             break;
374         }
375
376         ptmp->flags |= DIR_SCAN;        /* Mark this service for loop detection */
377
378         /*
379          * If there are links in the links included, follow them
380          */
381         np_list_for_each(dent, l_list) {
382             dir_t * target = getlink(dent)->target;
383             handle_t * ptrg = (mode == 'K') ? &target->stopp : &target->start;
384
385             if ((peg->run.lvl & ptrg->run.lvl) == 0)
386                 continue;                       /* Not same boot level */
387
388             if (target == tmp)
389                 break;                          /* Loop avoided */
390         
391             if (target == dir)
392                 break;                          /* Loop avoided */
393         
394             if (skip && skip == target) {
395                 if (!remembernode(pskp) || !remembernode(ptmp))
396                     loop_warn_two(pskp, ptmp, act);
397                 recursion = false;
398                 break;                          /* Loop detected, stop recursion */
399             }
400
401             if (ptrg->deep >= deep)             /* Nothing new */
402                 continue;
403                                                 /* The inner recursion */
404             __follow(target, tmp, deep, mode, reportloop);
405             prefetch(dent->next);
406
407             /* Just for the case an inner recursion was stopped */
408             if (loop_check(ptrg) || loop_check(ptmp) || loop_check(pskp)) {
409                 recursion = false;
410                 break;                          /* Loop detected, stop recursion */
411             }
412         }
413
414         ptmp->flags &= ~DIR_SCAN;       /* Remove loop detection mark */
415         prefetch(l_list->next);
416
417         if (!recursion) {
418             if (reportloop && !(ptmp->flags & DIR_LOOPREPORT)) {
419                 warn(" loop involving service %s at depth %d\n", tmp->name, level);
420                 ptmp->flags |= DIR_LOOPREPORT;
421             }
422             break;                      /* Loop detected, stop recursion */
423         }
424     }
425 out:
426     return;                     /* Make compiler happy */
427 }
428
429 #undef loop_warn_two
430 #undef loop_warn_one
431 #undef loop_check
432
433 /*
434  * Helper for follow_all: start with depth one.
435  */
436 static inline void follow(dir_t *restrict dir, const char mode, const char reportloop) attribute((always_inline,nonnull(1)));
437 static inline void follow(dir_t *restrict dir, const char mode, const char reportloop)
438 {
439     const int deep = (mode == 'K') ? (dir->stopp.mindeep) : (dir->start.mindeep);
440     /* Link depth starts here with one */
441     __follow(dir, (dir_t*)0, deep, mode, reportloop);
442 }
443
444 /*
445  * Put not existing services into a guessed order.
446  * The maximal order of not existing services can be
447  * set if they are required by existing services.
448  */
449 static void guess_order(dir_t *restrict dir, const char mode) attribute((nonnull(1)));
450 static void guess_order(dir_t *restrict dir, const char mode)
451 {
452     handle_t * peg  = (mode == 'K') ? &dir->stopp : &dir->start;
453     list_t * l_list = &peg->link;
454     register int min = 99;
455     register int deep = 0;
456     ushort lvl = 0;
457
458     if (dir->script)    /* Skip it because we have read it */
459         goto out;
460
461     if (*dir->name == '$') {    /* Don't touch our system facilities */
462         warn("System facilities not fully expanded, see %s!\n", dir->name);
463         goto out;
464     }
465
466     /* No full loop required because we seek for the lowest order */
467     if (!list_empty(l_list)) {
468         dir_t * target = getnextlink(l_list);
469         handle_t * ptrg = (mode == 'K') ? &target->stopp : &target->start;
470         uchar * order = &ptrg->deep;
471         list_t * dent;
472
473         if (min > *order)
474             min = *order;
475
476         lvl |= ptrg->run.lvl;
477
478         list_for_each_prev(dent, l_list) {
479             dir_t * tmp = getlink(dent)->target;
480             handle_t * ptmp = (mode == 'K') ? &tmp->stopp : &tmp->start;
481             uchar * order = &ptmp->deep;
482
483             if (++deep > MAX_DEEP)
484                 break;
485
486             if (target == dir)
487                 break;          /* Loop detected */
488
489             if (min > *order)
490                 min = *order;
491
492             lvl |= ptmp->run.lvl;
493         }
494         if (min > 1) {          /* Set guessed order of this unknown script */
495             uchar * order = &peg->deep;
496             *order = min - 1;
497             peg->run.lvl |= lvl;        /* Set guessed runlevels of this unknown script */
498         } else {
499             peg->run.lvl  = LVL_BOOT;
500         }
501     }
502 out:
503     return;
504 }
505
506 /*
507  * Sort linked list of provides into start or stop order
508  * during this set new start or stop order of the serives.
509  */
510 #undef SORT_REQUESTS
511 void lsort(const char type)
512 {
513     list_t sort = { &sort, &sort };
514 #ifdef SORT_REQUESTS
515     list_t * this;
516 #endif /* SORT_REQUESTS */
517     int order;
518
519     switch (type) {
520     case 'K':
521         for (order = 0; order <= maxstop; order++) {
522             list_t * ptr, * safe;
523             list_for_each_safe(ptr, safe, d_start) {
524                 dir_t * dir = getdir(ptr);
525                 if (dir->stopp.deep == order)
526                     move_tail(ptr, &sort);
527             }
528         }
529         join(&sort, d_start);
530 #ifdef SORT_REQUESTS
531         list_for_each(this, s_start) {
532             service_t * serv = getservice(this);
533             if (serv->attr.flags & SERV_DUPLET)
534                 continue;
535             initial(&sort);
536             for (order = 0; order <= maxstop; order++) {
537                 list_t * ptr, * safe;
538                 list_for_each_safe(ptr, safe, &serv->sort.rev) {
539                     req_t * rev = getreq(ptr);
540                     dir_t * dir = (dir_t*)rev->serv->dir;
541                     if (dir->stopp.deep == order)
542                         move_tail(ptr, &sort);
543                 }
544             }
545             join(&sort, &serv->sort.rev);
546         }
547 #endif /* SORT_REQUESTS */
548         break;
549     default:
550         for (order = 0; order <= maxstart; order++) {
551             list_t * ptr, * safe;
552             list_for_each_safe(ptr, safe, d_start) {
553                 dir_t * dir = getdir(ptr);
554                 if (dir->start.deep == order)
555                     move_tail(ptr, &sort);
556             }
557         }
558         join(&sort, d_start);
559 #ifdef SORT_REQUESTS
560         list_for_each(this, s_start) {
561             service_t * serv = getservice(this);
562             if (serv->attr.flags & SERV_DUPLET)
563                 continue;
564             initial(&sort);
565             for (order = 0; order <= maxstart; order++) {
566                 list_t * ptr, * safe;
567                 list_for_each_safe(ptr, safe, &serv->sort.req) {
568                     req_t * req = getreq(ptr);
569                     dir_t * dir = (dir_t*)req->serv->dir;
570                     if (dir->start.deep == order)
571                         move_tail(ptr, &sort);
572                 }
573             }
574             join(&sort, &serv->sort.req);
575         }
576 #endif /* SORT_REQUESTS */
577         break;
578     }
579
580
581 }
582
583 /*
584  * Clear out aliases of existing services, that is that for *one* script there
585  * exist several provides which could have have been required different by
586  * other services.  This avoids doing the same work several times.
587  */ 
588 void nickservice(service_t *restrict orig, service_t *restrict nick)
589 {
590     dir_t * dir = (dir_t*)orig->dir;
591     dir_t * cmp = (dir_t*)nick->dir;
592     list_t * dent, * safe;
593
594     if (dir == cmp)
595         return;
596
597     if (cmp->script && cmp->script != dir->script)
598         return;
599
600     list_for_each_safe(dent, safe, &cmp->start.link) {
601         link_t * link  = getlink(dent);
602         dir_t * target = link->target;
603
604         if (target == cmp)
605             continue;
606
607         ln_sf(target, dir, 'S');
608
609         /* remove the link from local link list but never free the target */
610
611         delete(dent);
612         free(link);
613     }
614
615     list_for_each_safe(dent, safe, &cmp->stopp.link) {
616         link_t * link  = getlink(dent);
617         dir_t * target = link->target;
618
619         if (target == cmp)
620             continue;
621
622         ln_sf(target, dir, 'K');
623
624         /* remove the link from local link list but never free the target */
625
626         delete(dent);
627         free(link);
628     }
629
630     delete(&cmp->d_list);       /* remove alias entry from global service list */
631
632                                 /* remember levels of old start handle */
633     dir->start.run.lvl |= cmp->start.run.lvl;
634     dir->start.flags   |= cmp->start.flags;
635
636                                 /* remember levels of old stop handle */
637     dir->stopp.run.lvl |= cmp->stopp.run.lvl;
638     dir->stopp.flags   |= cmp->stopp.flags;
639
640                                 /* remember global flags of old provide */
641     orig->attr.flags |= nick->attr.flags;
642     nick->attr.flags |= SERV_DUPLET;
643
644     if (cmp->script && cmp->script != dir->script) {
645         free(nick->attr.script);
646         nick->attr.script = orig->attr.script;
647     }
648
649     nick->dir   = (void*)dir;   /* remember main provide */
650     nick->start = &dir->start.run;
651     nick->stopp = &dir->stopp.run;
652
653     if (--cmp->ref <= 0) free(cmp);
654
655     list_for_each_safe(dent, safe, &nick->sort.req) {
656         req_t * this = getreq(dent);
657         boolean ok = true;
658         list_t * req;
659         list_for_each(req, &orig->sort.req) {
660             if (!strcmp(this->serv->name,getreq(req)->serv->name)) {
661                 ok = false;
662                 break;
663             }
664         }
665         if (!ok) {
666             delete(dent);
667             free(this);
668         } else
669             move_tail(dent, &orig->sort.req);
670     }
671
672     list_for_each_safe(dent, safe, &nick->sort.rev) {
673         req_t * this = getreq(dent);
674         boolean ok = true;
675         list_t * rev;
676         list_for_each(rev, &orig->sort.rev) {
677             if (!strcmp(this->serv->name,getreq(rev)->serv->name)) {
678                 ok = false;
679                 break;
680             }
681         }
682         if (!ok) {
683             delete(dent);
684             free(this);
685         } else
686             move_tail(dent, &orig->sort.rev);
687     }
688 }
689
690 void clear_all(void)
691 {
692     list_t * this;
693
694     /*
695      * Find dangling links in global service list and remove them
696      * if we by detect the remove bit from set above in the flags.
697      */
698
699     list_for_each(this, d_start) {
700         dir_t *dir = getdir(this);
701         list_t *dent, *hold;
702
703         list_for_each_safe(dent, hold, &dir->start.link) {
704             link_t * link  = getlink(dent);
705             dir_t * target = link->target;
706
707             if (target == dir)
708                 continue;
709
710             if ((attof(target)->flags & SERV_DUPLET) == 0)
711                 continue;
712
713             /* remove the link from local link list */
714
715             delete(dent);
716             free(link);
717
718             /* 
719              * Do not free allocated strings and structure if in use
720              * never free cmp->attr.script as this remains always in use.
721              */
722
723             if (--target->ref <= 0) free(target);
724         }
725
726         list_for_each_safe(dent, hold, &dir->stopp.link) {
727             link_t * link  = getlink(dent);
728             dir_t * target = link->target;
729
730             if (target == dir)
731                 continue;
732
733             if ((attof(target)->flags & SERV_DUPLET) == 0)
734                 continue;
735
736             /* remove the link from local link list */
737
738             delete(dent);
739             free(link);
740
741             /* 
742              * Do not free allocated strings and structure if in use
743              * never free cmp->attr.script as this remains always in use.
744              */
745
746             if (--target->ref <= 0) free(target);
747         }
748     }
749 #if defined(DEBUG) && (DEBUG > 0)
750     list_for_each(this, s_start) {
751         service_t * srv = getservice(this);
752         list_t * nxt, * hold;
753
754         if (srv->attr.flags & SERV_DUPLET)
755             continue;
756
757         list_for_each_safe(nxt, hold, s_start) {
758             list_t * dent, * safe;
759             service_t * orv;
760
761             orv = getservice(nxt);
762
763             if ((orv->attr.flags & SERV_DUPLET) == 0)
764                 continue;
765
766             if (srv->dir != orv->dir)
767                 continue;
768
769             srv->attr.flags |= orv->attr.flags;
770             srv->attr.flags &= ~SERV_DUPLET;
771
772             list_for_each_safe(dent, safe, &orv->sort.req) {
773                 req_t * this = getreq(dent);
774                 boolean ok = true;
775                 list_t * req;
776                 list_for_each(req, &srv->sort.req) {
777                     if (!strcmp(this->serv->name,getreq(req)->serv->name)) {
778                         ok = false;
779                         break;
780                     }
781                 }
782                 if (!ok) {
783                    fprintf(stderr, "BUG: removed %s from start list of %s, missed getorig()?\n",
784                            this->serv->name, orv->name);
785                    delete(dent);
786                    free(this);
787                 } else {
788                    fprintf(stderr, "BUG: moved %s from start list of %s to %s, missed getorig()?\n",
789                            this->serv->name, orv->name, srv->name);
790                    move_tail(dent, &srv->sort.req);
791                 }
792             }
793
794             list_for_each_safe(dent, safe, &orv->sort.rev) {
795                 req_t * this = getreq(dent);
796                 boolean ok = true;
797                 list_t * rev;
798                 list_for_each(rev, &srv->sort.rev) {
799                    if (!strcmp(this->serv->name,getreq(rev)->serv->name)) {
800                         ok = false;
801                         break;
802                    }
803                 }
804                 if (!ok) {
805                    fprintf(stderr, "BUG: removed %s from start list of %s, missed getorig()?\n",
806                            this->serv->name, orv->name);
807                    delete(dent);
808                    free(this);
809                 } else {
810                    fprintf(stderr, "BUG: moved %s from start list of %s to %s, missed getorig()?\n",
811                            this->serv->name, orv->name, srv->name);
812                    move_tail(dent, &srv->sort.rev);
813                 }
814             }
815         }
816     }
817 #endif
818 }
819
820 /*
821  * Follow all services and their dependencies recursivly.
822  */
823 void follow_all(void)
824 {
825     list_t *tmp;
826
827     /*
828      * Follow all scripts and calculate the main ordering.
829      */
830     list_for_each(tmp, d_start) {
831         maxorder = &maxstart;
832         follow(getdir(tmp), 'S', 1);
833         maxorder = &maxstop;
834         follow(getdir(tmp), 'K', 1);
835     }
836
837     /*
838      * Guess order of not installed scripts in comparision
839      * to the well known scripts.
840      */
841     list_for_each(tmp, d_start) {
842         maxorder = &maxstart;
843         guess_order(getdir(tmp), 'S');
844         maxorder = &maxstart;
845         guess_order(getdir(tmp), 'K');
846     }
847 }
848
849 boolean is_loop_detected(void)
850 {
851     list_t *tmp;
852     list_for_each(tmp, d_start) {
853         dir_t * dir = getdir(tmp);
854         if (dir->start.flags & DIR_LOOPREPORT)
855             return true;
856         if (dir->stopp.flags  & DIR_LOOPREPORT)
857             return true;
858     }
859     return false;
860 }
861
862 /*
863  * For debuging: show all services
864  */
865 #if defined(DEBUG) && (DEBUG > 0)
866 void show_all()
867 {
868     list_t *tmp;
869     if (maxstop > 0) list_for_each(tmp, d_start) {
870         char * script, *name, *lvlstr;
871         dir_t * dir = getdir(tmp);
872         handle_t * peg;
873         uchar deep;
874         ushort lvl;
875         if (!dir)
876             continue;
877         name = dir->name;
878         peg  = &dir->stopp;
879         lvl  = peg->run.lvl;
880         deep = peg->deep;
881         if (attof(dir)->script)
882             script = attof(dir)->script;
883         else if (*name == '$')
884             script = "%system";
885         else
886             script = "%guessed";
887         lvlstr = lvl2str(lvl);
888         info("K%.2d %s 0x%.2x '%s' (%s)\n", deep, name, lvl, lvlstr, script);
889         xreset(lvlstr);
890     }
891     if (maxstart > 0) list_for_each(tmp, d_start) {
892         char * script, *name, *lvlstr;
893         dir_t * dir = getdir(tmp);
894         handle_t * peg;
895         uchar deep;
896         ushort lvl;
897         if (!dir)
898             continue;
899         name = dir->name;
900         peg  = &dir->start;
901         lvl  = peg->run.lvl;
902         deep = peg->deep;
903         if (attof(dir)->script)
904             script = attof(dir)->script;
905         else if (*name == '$')
906             script = "%system";
907         else
908             script = "%guessed";
909         lvlstr = lvl2str(lvl);
910         info("S%.2d %s 0x%.2x '%s' (%s)\n", deep, name, lvl, lvlstr, script);
911         xreset(lvlstr);
912     }
913 }
914 #endif
915
916 /*
917  * Used within loops to get scripts not included in this runlevel
918  */
919 boolean notincluded(const char *restrict const script, const char mode, const int runlevel)
920 {
921     list_t *tmp;
922     boolean ret = false;
923     const ushort lvl = map_runlevel_to_lvl (runlevel);
924
925     list_for_each_prev(tmp, d_start) {
926         dir_t * dir = getdir(tmp);
927         level_t * run = (mode == 'K') ? &dir->stopp.run : &dir->start.run;
928
929         if (run->lvl & lvl)             /* Same runlevel */
930             continue;
931
932         if (dir->script == (char*)0)    /* No such file */
933             continue;
934
935         if (strcmp(script, dir->script))
936             continue;                   /* Not this file */
937
938         ret = true;                     /* Not included */
939         break;
940     }
941
942     return ret;
943 }
944
945 /*
946  * Used within loops to list services an for a given runlevel bit mask.
947  */
948 service_t * listscripts(const char **restrict script, const char mode, const ushort lvl)
949 {
950     static list_t * tmp;
951     service_t * serv;
952     ushort level;
953     dir_t * dir;
954
955     if (!*script)
956         tmp  = d_start->next;
957
958     do {
959         serv = (service_t*)0;
960         if (tmp == d_start)
961             break;
962         prefetch(tmp->next);
963         dir = getdir(tmp);
964
965         attof(dir)->korder = dir->stopp.deep;
966         attof(dir)->sorder = dir->start.deep;
967
968         serv = dir->serv;
969         *script = serv->attr.script;
970
971         switch (mode) {
972         case 'X':
973             level = (dir->stopp.run.lvl|dir->start.run.lvl);
974             break;
975         case 'K':
976             level = dir->stopp.run.lvl;
977             break;
978         default:
979             level = dir->start.run.lvl;
980             break;
981         }
982
983         tmp = tmp->next;
984
985     } while ((*script == (char*)0) || (level & lvl) == 0);
986
987     return serv;
988 }
989
990 /*
991  * THIS services DEPENDS on that service befor startup or shutdown.
992  */
993 void requires(service_t *restrict this, service_t *restrict dep, const char mode)
994 {
995     ln_sf((dir_t*)this->dir, (dir_t*)dep->dir, mode);
996 }
997
998 /*
999  * Set the runlevels of a service.
1000  */
1001 void runlevels(service_t *restrict serv, const char mode, const char *restrict lvl)
1002 {
1003     dir_t * dir   = (dir_t *)serv->dir;
1004     handle_t * peg = (mode == 'K') ? &dir->stopp : &dir->start;
1005     peg->run.lvl |= str2lvl(lvl);
1006 }
1007
1008 /*
1009  * Reorder all services starting with a service
1010  * being in same runlevels.
1011  */
1012 void setorder(const char *restrict script, const char mode, const int order, const boolean recursive)
1013 {
1014     dir_t * dir = findscript(script);
1015     handle_t * peg;
1016     list_t * tmp;
1017
1018     if (!dir)
1019         goto out;
1020
1021     if (mode == 'K') {
1022         peg = &dir->stopp;
1023         maxorder = &maxstop;
1024     } else {
1025         peg = &dir->start;
1026         maxorder = &maxstart;
1027     }
1028
1029     if (peg->mindeep < order)
1030         peg->mindeep = order;           /* Remember lowest default order deep */
1031
1032     if (peg->deep >= peg->mindeep)      /* Nothing to do */
1033         goto out;
1034
1035     if (!recursive) {
1036         peg->deep = peg->mindeep;
1037         goto out;
1038     }
1039
1040     /*
1041      * Follow the script and re-calculate the ordering.
1042      */
1043     __follow(dir, (dir_t*)0, peg->mindeep, mode, 0);
1044
1045     /*
1046      * Guess order of not installed scripts in comparision
1047      * to the well known scripts.
1048      */
1049     list_for_each(tmp, d_start)
1050         guess_order(getdir(tmp), mode);
1051 out:
1052     return;
1053 }
1054
1055 /*
1056  * Get the order of a script.
1057  */
1058 int getorder(const char *restrict script, const char mode)
1059 {
1060     dir_t * dir = findscript(script);
1061     int order = 0;
1062
1063     if (dir) {
1064         handle_t * peg = (mode == 'K') ? &dir->stopp : &dir->start;
1065         order = peg->deep;
1066     }
1067
1068     return order;
1069 }
1070
1071 /*
1072  * Provide a service if the corresponding script
1073  * was read and the scripts name was remembered.
1074  * A given script name marks a service as a readed one.
1075  * One script and several provided facilities leads
1076  * to the same order for those facilities.
1077  */
1078 boolean makeprov(service_t *restrict serv, const char *restrict script)
1079 {
1080     dir_t *restrict alias = findscript(script);
1081     dir_t *restrict dir   = (dir_t *restrict)serv->dir;
1082     boolean ret = true;
1083
1084     if (!dir->script) {
1085         list_t * ptr;
1086         if (!alias) {
1087             serv->attr.script = xstrdup(script);
1088             serv->attr.flags |= SERV_SCRIPT;
1089             dir->script = serv->attr.script;
1090         } else
1091             dir->script = alias->script;
1092
1093         list_for_each(ptr, s_start) {
1094             service_t * tmp = getservice(ptr);
1095             if (tmp == serv)
1096                 continue;
1097             if (tmp->dir != serv->dir)
1098                 continue;
1099             if (tmp->attr.script)
1100                 continue;
1101             tmp->attr.script = dir->script;
1102             tmp->attr.flags |= SERV_SCRIPT;
1103         }
1104
1105     } else if (strcmp(dir->script, script))
1106         ret = false;
1107
1108     return ret;
1109 }
1110
1111 /*
1112  * Find the script name of a provided feature
1113  */
1114 const char * getscript(const char *restrict prov)
1115 {
1116     char * script = (char*)0;
1117     list_t * ptr;
1118
1119     list_for_each(ptr, s_start) {
1120         service_t * this = getservice(ptr);
1121         if (!strcmp(this->name, prov)) {
1122             if (this->attr.script)
1123                 script = this->attr.script;
1124             break;
1125         }
1126     }
1127     return script;
1128 }
1129
1130 /*
1131  * Return the provided service of a given script
1132  */
1133 const char * getprovides(const char *restrict script)
1134 {
1135     const dir_t * dir = findscript(script);
1136     const char * prov = (const char*)0;
1137
1138     if (dir)
1139         prov = dir->name;
1140     return prov;
1141 }
1142
1143 /*
1144  * Find a specific service by its name
1145  */
1146 service_t * findservice(const char *restrict const name)
1147 {
1148     list_t * ptr;
1149     service_t * ret = (service_t*)0;
1150
1151     if (name == (const char*)0)
1152         goto out;
1153
1154     list_for_each(ptr, s_start) {
1155         service_t * this = getservice(ptr);
1156         if (!strcmp(this->name, name)) {
1157             ret = this;
1158             break;
1159         }
1160     }
1161 out:
1162     return ret;
1163 }