Git init
[external/insserv.git] / insserv.c
1 /*
2  * insserv(.c)
3  *
4  * Copyright 2000-2008 Werner Fink, 2000 SuSE GmbH Nuernberg, Germany,
5  *                                  2003 SuSE Linux AG, Germany.
6  *                                  2004 SuSE LINUX AG, Germany.
7  *                             2005-2008 SUSE LINUX Products GmbH, Germany.
8  * Copyright 2005,2008 Petter Reinholdtsen
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  */
15
16 #define MINIMAL_MAKE    1       /* Remove disabled scripts from .depend.boot,
17                                  * .depend.start, .depend.halt, and .depend.stop */
18 #define MINIMAL_RULES   1       /* ditto */
19
20 #include <pwd.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <fcntl.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/syscall.h>
32 #include <dirent.h>
33 #include <regex.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <getopt.h>
37 #if defined(USE_RPMLIB) && (USE_RPMLIB > 0)
38 # include <rpm/rpmlib.h>
39 # include <rpm/rpmmacro.h>
40 #endif /* USE_RPMLIB */
41 #include "listing.h"
42
43 #ifdef SUSE
44 # define DEFAULT_START_LVL      "3 5"
45 # define DEFAULT_STOP_LVL       "3 5"
46 # define USE_KILL_IN_BOOT       1
47 # define USE_COMPAT_EMPTY       1
48 static inline void oneway(char *restrict stop) attribute((always_inline,nonnull(1)));
49 static inline void oneway(char *restrict stop)
50 {
51     char * ptr = stop;
52     while ((ptr = strpbrk(ptr, "016sS")))
53         *ptr++ = ' ';
54 }
55 #else /* not SUSE, but Debian */
56 # define DEFAULT_START_LVL      "2 3 4 5"
57 # define DEFAULT_STOP_LVL       "0 1 6"
58 # define DEFAULT_DEPENDENCY     "$remote_fs $syslog"
59 # undef  USE_KILL_IN_BOOT
60 # undef  USE_COMPAT_EMPTY
61 #endif /* not SUSE, but Debian */
62
63 #ifndef  INITDIR
64 # define INITDIR        "/etc/init.d"
65 #endif
66 #ifndef  OVERRIDEDIR
67 # define OVERRIDEDIR    "/etc/insserv/overrides"
68 #endif
69 #ifndef  INSCONF
70 # define INSCONF        "/etc/insserv.conf"
71 #endif
72
73 /*
74  * For a description of regular expressions see regex(7).
75  */
76 #define COMM            "^#[[:blank:]]*"
77 #define VALUE           ":[[:blank:]]*([[:print:]]*)"
78 /* The second substring contains our value (the first is all) */
79 #define SUBNUM          2
80 #define SUBNUM_SHD      3
81 #define START           "[-_]+start"
82 #define STOP            "[-_]+stop"
83
84 /* The main regular search expressions */
85 #define PROVIDES        COMM "provides" VALUE
86 #define REQUIRED        COMM "required"
87 #define SHOULD          COMM "(x[-_]+[a-z0-9_-]*)?should"
88 #define BEFORE          COMM "(x[-_]+[a-z0-9_-]*)?start[-_]+before"
89 #define AFTER           COMM "(x[-_]+[a-z0-9_-]*)?stop[-_]+after"
90 #define DEFAULT         COMM "default"
91 #define REQUIRED_START  REQUIRED START VALUE
92 #define REQUIRED_STOP   REQUIRED STOP  VALUE
93 #define SHOULD_START    SHOULD   START VALUE
94 #define SHOULD_STOP     SHOULD   STOP  VALUE
95 #define START_BEFORE    BEFORE   VALUE
96 #define STOP_AFTER      AFTER    VALUE
97 #define DEFAULT_START   DEFAULT  START VALUE
98 #define DEFAULT_STOP    DEFAULT  STOP  VALUE
99 #define DESCRIPTION     COMM "description" VALUE
100
101 /* System facility search within /etc/insserv.conf */
102 #define EQSIGN          "([[:blank:]]*[=:][[:blank:]]*|[[:blank:]]+)"
103 #define CONFLINE        "^(\\$[a-z0-9_-]+)" EQSIGN "([[:print:]]*)"
104 #define CONFLINE2       "^(<[a-z0-9_-]+>)"  EQSIGN "([[:print:]]*)"
105 #define SUBCONF         2
106 #define SUBCONFNUM      4
107
108 /* The root file system */
109 static char *root;
110
111 /* The main line buffer if unique */
112 static char buf[LINE_MAX];
113
114 /* When to be verbose */
115 static boolean verbose = false;
116
117 /* When to be verbose */
118 static boolean dryrun = false;
119
120 /* When paths set do not add root if any */
121 static boolean set_override = false;
122 static boolean set_insconf = false;
123
124 /* Search results points here */
125 typedef struct lsb_struct {
126     char *provides;
127     char *required_start;
128     char *required_stop;
129     char *should_start;
130     char *should_stop;
131     char *start_before;
132     char *stop_after;
133     char *default_start;
134     char *default_stop;
135     char *description;
136 } attribute((aligned(sizeof(char*)))) lsb_t;
137
138 /* Search results points here */
139 typedef struct reg_struct {
140     regex_t prov;
141     regex_t req_start;
142     regex_t req_stop;
143     regex_t shl_start;
144     regex_t shl_stop;
145     regex_t start_bf;
146     regex_t stop_af;
147     regex_t def_start;
148     regex_t def_stop;
149     regex_t desc;
150 } attribute((aligned(sizeof(regex_t)))) reg_t;
151
152 typedef struct creg_struct {
153     regex_t isysfaci;
154     regex_t isactive;
155 } attribute((aligned(sizeof(regex_t)))) creg_t;
156
157 static lsb_t script_inf;
158 static reg_t reg;
159 static creg_t creg;
160 static char empty[1] = "";
161
162 /* Delimeters used for spliting results with strsep(3) */
163 const char *const delimeter = " ,;\t";
164
165 /*
166  * push and pop directory changes: pushd() and popd()
167  */
168 typedef struct pwd_struct {
169     list_t      deep;
170     char        *pwd;
171 } __align pwd_t;
172 #define getpwd(list)    list_entry((list), struct pwd_struct, deep)
173
174 static list_t pwd = { &pwd, &pwd }, * topd = &pwd;
175
176 static void pushd(const char *restrict const path) attribute((nonnull(1)));
177 static void pushd(const char *restrict const path)
178 {
179     pwd_t *restrict dir;
180
181     if (posix_memalign((void*)&dir, sizeof(void*), alignof(pwd_t)) == 0) {
182         if (!(dir->pwd = getcwd((char*)0, 0)))
183             goto err;
184         insert(&dir->deep, topd->prev);
185         if (chdir(path) < 0)
186             goto err;
187         return;
188     }
189 err:
190     error ("pushd() can not change to directory %s: %s\n", path, strerror(errno));
191 }
192
193 static void popd(void)
194 {
195     pwd_t * dir;
196
197     if (list_empty(topd))
198         goto out;
199     dir = getpwd(topd->prev);
200     if (chdir(dir->pwd) < 0)
201         error ("popd() can not change directory %s: %s\n", dir->pwd, strerror(errno));
202     delete(topd->prev);
203     free(dir->pwd);
204     free(dir);
205 out:
206     return;
207 }
208
209 /*
210  * Linked list of system facilities services and their replacment
211  */
212 typedef struct string {
213     int *restrict ref;
214     char        *name;
215 } __align string_t;
216
217 typedef struct repl {
218     list_t     r_list;
219     string_t     r[1];
220 } __align repl_t;
221 #define getrepl(arg)    list_entry((arg), struct repl, r_list)
222
223 typedef struct faci {
224     list_t       list;
225     list_t    replace;
226     char        *name;
227 } __align faci_t;
228 #define getfaci(arg)    list_entry((arg), struct faci, list)
229
230 static list_t sysfaci = { &sysfaci, &sysfaci }, *sysfaci_start = &sysfaci;
231
232 /*
233  * Remember requests for required or should services and expand `$' token
234  */
235 static void rememberreq(service_t *restrict serv, uint bit,
236                         const char *restrict required) attribute((noinline,nonnull(1,3)));
237 static void rememberreq(service_t * restrict serv, uint bit, const char * restrict required)
238 {
239     const char type = (bit & REQ_KILL) ? 'K' : 'S';
240     const char * token;
241     char * tmp = strdupa(required);
242     list_t * ptr, * list;
243     ushort old = bit;
244
245     if (!tmp)
246         error("%s", strerror(errno));
247
248     while ((token = strsep(&tmp, delimeter)) && *token) {
249         service_t * req, * here, * need;
250         boolean found = false;
251
252         bit = old;
253
254         switch(*token) {
255         case '+':
256             /* This is an optional token */
257             token++;
258             bit &= ~REQ_MUST;
259             bit |=  REQ_SHLD;
260         default:
261             req = addservice(token);
262             if (bit & REQ_KILL) {
263                 req  = getorig(req);
264                 list = &req->sort.rev;
265                 here = req;
266                 need = serv;
267             } else {
268                 serv = getorig(serv);
269                 list = &serv->sort.req;
270                 here = serv;
271                 need = req;
272             }
273             np_list_for_each(ptr, list) {
274                 if (!strcmp(getreq(ptr)->serv->name, need->name)) {
275                     getreq(ptr)->flags |= bit;
276                     found = true;
277                     break;
278                 }
279             }
280             if (!found) {
281                 req_t *restrict this;
282                 if (posix_memalign((void*)&this, sizeof(void*), alignof(req_t)) != 0)
283                     error("%s", strerror(errno));
284                 memset(this, 0, alignof(req_t));
285                 insert(&this->list, list->prev);
286                 this->flags = bit;
287                 this->serv = need;
288             }
289             /* Expand requested services for sorting */
290             requires(here, need, type);
291             break;
292         case '$':
293             if (strcasecmp(token, "$all") == 0) {
294                 serv->attr.flags |= SERV_ALL;
295                 break;
296             }
297             /* Expand the `$' token recursively down */
298             list_for_each(ptr, sysfaci_start) {
299                 if (!strcmp(token, getfaci(ptr)->name)) {
300                     list_t * lst;
301                     np_list_for_each(lst, &getfaci(ptr)->replace)
302                         rememberreq(serv, bit, getrepl(lst)->r[0].name);
303                     break;
304                 }
305             }
306             break;
307         }
308     }
309 }
310
311 static void reversereq(service_t *restrict serv, uint bit,
312                        const char *restrict list) attribute((noinline,nonnull(1,3)));
313 static void reversereq(service_t *restrict serv, uint bit, const char *restrict list)
314 {
315     const char * token;
316     char * tmp = strdupa(list);
317     ushort old = bit;
318
319     if (!tmp)
320         error("%s", strerror(errno));
321
322     while ((token = strsep(&tmp, delimeter)) && *token) {
323         service_t * rev;
324         list_t * ptr;
325
326         bit = old;
327
328         switch (*token) {
329         case '+':
330             token++;
331             bit &= ~REQ_MUST;
332             bit |=  REQ_SHLD;
333         default:
334             rev = addservice(token);
335             rememberreq(rev, bit, serv->name);
336             break;
337         case '$':
338             list_for_each(ptr, sysfaci_start) {
339                 if (!strcmp(token, getfaci(ptr)->name)) {
340                     list_t * lst;
341                     np_list_for_each(lst, &getfaci(ptr)->replace)
342                         reversereq(serv, bit, getrepl(lst)->r[0].name);
343                     break;
344                 }
345             }
346             break;
347         }
348     }
349 }
350
351 /*
352  * Check required services for name
353  */
354 static boolean chkrequired(service_t *restrict serv) attribute((nonnull(1)));
355 static boolean chkrequired(service_t *restrict serv)
356 {
357     boolean ret = true;
358     list_t * pos;
359
360     if (!serv)
361         goto out;
362     serv = getorig(serv);
363
364     np_list_for_each(pos, &serv->sort.req) {
365         req_t *req = getreq(pos);
366         service_t * must;
367
368         if ((req->flags & REQ_MUST) == 0)
369             continue;
370         must = req->serv;
371         must = getorig(must);
372
373         if ((must->attr.flags & (SERV_CMDLINE|SERV_ENABLED)) == 0) {
374             warn("Service %s has to be enabled to start service %s\n",
375                  req->serv->name, serv->name);
376             ret = false;
377         }
378     }
379 #if 0
380     if (serv->attr.flags & (SERV_CMDLINE|SERV_ENABLED))
381         goto out;
382     np_list_for_each(pos, &serv->sort.rev) {
383         req_t *rev = getreq(pos);
384         service_t * must;
385
386         if ((rev->flags & REQ_MUST) == 0)
387             continue;
388         must = rev->serv;
389         must = getorig(must);
390
391         if (must->attr.flags & (SERV_CMDLINE|SERV_ENABLED)) {
392             warn("Service %s has to be enabled to stop service %s\n",
393                  serv->name, rev->serv->name);
394             ret = false;
395         }
396     }
397 #endif
398 out:
399     return ret;
400 }
401
402 /*
403  * Check dependencies for name as a service
404  */
405 static boolean chkdependencies(service_t *restrict serv) attribute((nonnull(1)));
406 static boolean chkdependencies(service_t *restrict serv)
407 {
408     const char * const name = serv->name;
409     boolean ret = true;
410     list_t * ptr;
411
412     list_for_each(ptr, s_start) {
413         service_t * cur = getservice(ptr);
414         list_t * pos;
415
416         if (!cur)
417             continue;
418
419         if ((cur->attr.flags & SERV_ENABLED) == 0)
420             continue;
421
422         if (cur->attr.flags & SERV_DUPLET)
423             continue;
424
425         if (list_empty(&cur->sort.req))
426             continue;
427
428         np_list_for_each(pos, &cur->sort.req) {
429             req_t *req = getreq(pos);
430             const ushort flags = req->serv->attr.flags;
431
432             if (!(req->flags & REQ_MUST))
433                 continue;
434
435             if (strcmp(req->serv->name, name) != 0)
436                 continue;
437
438             if ((cur->attr.flags & SERV_CMDLINE) && (flags & SERV_CMDLINE))
439                 continue;
440
441             warn("Service %s has to be enabled to start service %s\n",
442                  name, cur->name);
443             ret = false;
444         }
445     }
446     return ret;
447 }
448
449 /*
450  * This helps us to work out the current symbolic link structure
451  */
452 static inline service_t * current_structure(const char *const restrict this, const char order,
453                                      const int runlvl, const char type) attribute((always_inline,nonnull(1)));
454 static inline service_t * current_structure(const char *const this, const char order,
455                                      const int runlvl, const char type)
456 {
457     service_t * serv = addservice(this);
458     level_t * run;
459     ushort here = map_runlevel_to_lvl(runlvl);
460
461     if (type == 'K') {
462         run = serv->stopp;
463         if (!serv->attr.korder)
464             serv->attr.korder = 99;
465         if (serv->attr.korder > order)
466             serv->attr.korder = order;
467 #ifdef SUSE
468         /* This because SuSE boot script concept uses a differential link scheme. */
469         here &= ~LVL_ONEWAY;
470 #endif /* SUSE */
471     } else {
472         run = serv->start;
473         if (serv->attr.sorder < order)
474             serv->attr.sorder = order;
475     }
476     run->lvl |= here;
477
478     return serv;
479 }
480
481 static void setlsb(const char *restrict const name) attribute((unused));
482 static void setlsb(const char *restrict const name)
483 {
484     service_t * serv = findservice(name);
485     if (serv)
486         serv->attr.flags &= ~SERV_NOTLSB;
487 }
488
489 /*
490  * This helps us to set none LSB conform scripts to required
491  * max order, therefore we set a dependency to the first
492  * lsb conform service found in current link scheme.
493  */
494 static inline void nonlsb_script(void) attribute((always_inline));
495 static inline void nonlsb_script(void)
496 {
497     list_t * pos;
498
499     list_for_each(pos, s_start) {
500         if (getservice(pos)->attr.flags & SERV_NOTLSB) {
501             service_t * req, * srv = getservice(pos);
502             list_t * tmp;
503             uchar max;
504
505             max = 0;
506             req = (service_t*)0;
507             list_for_each(tmp, s_start) {
508                 service_t * cur = getservice(tmp);
509                 if (cur->attr.flags & SERV_NOTLSB)
510                     continue;
511                 if ((cur->attr.flags & SERV_ENABLED) == 0)
512                     continue;
513                 if (!cur->attr.sorder)
514                     continue;
515                 if ((srv->start->lvl & cur->start->lvl) == 0)
516                     continue;
517                 if (cur->attr.sorder >= srv->attr.sorder)
518                     continue;
519                 if (max < cur->attr.sorder) {
520                     max = cur->attr.sorder;
521                     req = cur;
522                 }
523             }
524
525             if (req)
526                 requires(srv, req, 'S');
527
528             max = 99;
529             req = (service_t*)0;
530             list_for_each(tmp, s_start) {
531                 service_t * cur = getservice(tmp);
532                 if (cur->attr.flags & SERV_NOTLSB)
533                     continue;
534                 if ((cur->attr.flags & SERV_ENABLED) == 0)
535                     continue;
536                 if (!cur->attr.korder)
537                     continue;
538                 if ((srv->stopp->lvl & cur->stopp->lvl) == 0)
539                     continue;
540                 if (cur->attr.korder <= srv->attr.korder)
541                     continue;
542                 if (max > cur->attr.korder) {
543                     max = cur->attr.korder;
544                     req = cur;
545                 }
546             }
547
548             if (req)
549                 requires(req, srv, 'K');
550         }
551     }
552 }
553
554 /*
555  * This helps us to get interactive scripts to be the only service
556  * within on start or stop service group. Remaining problem is that
557  * if required scripts are missed the order can be wrong.
558  */
559 static inline void active_script(void) attribute((always_inline));
560 static inline void active_script(void)
561 {
562     list_t * pos;
563     int deep = 1;
564
565     for (deep = 0; deep < 100; deep++) {
566         list_for_each(pos, s_start) {
567             service_t * serv = getservice(pos);
568             list_t * tmp;
569
570             if (serv->attr.script == (char*)0)
571                 continue;
572
573             if ((serv->attr.flags & SERV_INTRACT) == 0)
574                 continue;
575
576             serv->attr.sorder = getorder(serv->attr.script, 'S');
577
578             if (serv->attr.sorder != deep)
579                 continue;
580
581             if (serv->attr.flags & SERV_DUPLET)
582                 continue;               /* Duplet */
583
584             list_for_each(tmp, s_start) {
585                 service_t * cur = getservice(tmp);
586                 const char * script;
587
588                 if (getorig(cur) == serv)
589                     continue;
590
591                 if ((serv->start->lvl & cur->start->lvl) == 0)
592                     continue;
593
594                 /*
595                  * Use real script name for getorder()/setorder()
596                  */
597                 if (cur->attr.script == (char*)0)
598                     continue;
599                 script = cur->attr.script;
600
601                 cur->attr.sorder = getorder(script, 'S');
602
603                 if (cur->attr.sorder != deep)
604                     continue;
605                 /*
606                  * Increase order of members of the same start
607                  * group and recalculate dependency order (`true')
608                  */
609                 setorder(script, 'S', ++cur->attr.sorder, true);
610             }
611         }
612     }
613 }
614
615 /*
616  * Last but not least the `$all' scripts will be set to the
617  * end of the current start order.
618  */
619 static inline void all_script(void) attribute((always_inline));
620 static inline void all_script(void)
621 {
622     list_t * pos;
623
624     list_for_each(pos, s_start) {
625         service_t * serv = getservice(pos);
626         list_t * tmp;
627         int neworder;
628
629         if (serv->attr.flags & SERV_DUPLET)
630             continue;                   /* Duplet */
631
632         if (!(serv->attr.flags & SERV_ALL))
633             continue;
634
635         if (serv->attr.script == (char*)0)
636             continue;
637
638         neworder = 0;
639         list_for_each(tmp, s_start) {
640             service_t * cur = getservice(tmp);
641
642             if (cur->attr.flags & SERV_DUPLET)
643                 continue;               /* Duplet */
644
645             if ((serv->start->lvl & cur->start->lvl) == 0)
646                 continue;
647
648             if (cur->attr.script == (char*)0)
649                 continue;
650
651             if (cur == serv)
652                 continue;
653
654             if (cur->attr.flags & SERV_ALL)
655                 continue;
656
657             cur->attr.sorder = getorder(cur->attr.script, 'S');
658
659             if (cur->attr.sorder > neworder)
660                 neworder = cur->attr.sorder;
661         }
662         neworder++;
663
664         if      (neworder > MAX_DEEP)
665             neworder = maxstart;
666         else if (neworder > maxstart)
667             maxstart = neworder;
668
669         setorder(serv->attr.script, 'S', neworder, false);
670     }
671 }
672
673 /*
674  * Make the dependency files
675  */
676 static inline void makedep(void) attribute((always_inline));
677 static inline void makedep(void)
678 {
679     FILE *boot, *start, *stop, *out;
680 #ifdef USE_KILL_IN_BOOT
681     FILE *halt;
682 #endif /* USE_KILL_IN_BOOT */
683     const char *target;
684     service_t *serv;
685
686     if (dryrun) {
687 #ifdef USE_KILL_IN_BOOT
688         info("dryrun, not creating .depend.boot, .depend.start, .depend.halt, and .depend.stop\n");
689 #else  /* not USE_KILL_IN_BOOT */
690         info("dryrun, not creating .depend.boot, .depend.start, and .depend.stop\n");
691 #endif /* not USE_KILL_IN_BOOT */
692         return;
693     }
694     if (!(boot  = fopen(".depend.boot",  "w"))) {
695         warn("fopen(.depend.stop): %s\n", strerror(errno));
696         return;
697     }
698
699     if (!(start = fopen(".depend.start", "w"))) {
700         warn("fopen(.depend.start): %s\n", strerror(errno));
701         fclose(boot);
702         return;
703     }
704
705     info("creating .depend.boot\n");
706     info("creating .depend.start\n");
707
708     lsort('S');                                 /* Sort into start order, set new sorder */
709
710     target = (char*)0;
711     fprintf(boot, "TARGETS =");
712     while ((serv = listscripts(&target, 'S', LVL_BOOT))) {
713         if (!serv)
714             continue;
715 #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
716         if (serv->attr.ref <= 0)
717             continue;
718 #endif /* MINIMAL_MAKE */
719         fprintf(boot, " %s", target);
720     }
721     fputc('\n', boot);
722
723     target = (char*)0;
724     fprintf(start, "TARGETS =");
725     while ((serv = listscripts(&target, 'S', LVL_ALL))) {       /* LVL_ALL: nearly all but not BOOT */
726         if (!serv)
727             continue;
728 #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
729         if (serv->attr.ref <= 0)
730             continue;
731 #endif /* MINIMAL_MAKE */
732         fprintf(start, " %s", target);
733     }
734     fputc('\n', start);
735
736     fprintf(boot,  "INTERACTIVE =");
737     fprintf(start, "INTERACTIVE =");
738
739     target = (char*)0;
740     while ((serv = listscripts(&target, 'S', LVL_BOOT|LVL_ALL))) {
741         if (!serv)
742             continue;
743 #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
744         if (serv->attr.ref <= 0)
745             continue;
746 #endif /* not MINIMAL_MAKE */
747
748         if (list_empty(&serv->sort.req))
749             continue;
750
751         if (serv->start->lvl & LVL_BOOT)
752             out = boot;
753         else
754             out = start;
755
756         if (serv->attr.flags & SERV_INTRACT)
757             fprintf(out, " %s", target);
758     }
759     fputc('\n', boot);
760     fputc('\n', start);
761
762     target = (char*)0;
763     while ((serv = listscripts(&target, 'S', LVL_BOOT|LVL_ALL))) {
764         boolean mark;
765         list_t * pos;
766
767         if (!serv)
768             continue;
769 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
770         if (serv->attr.ref <= 0)
771             continue;
772 #endif /* not MINIMAL_RULES */
773
774         if (list_empty(&serv->sort.req))
775             continue;
776
777         if (serv->start->lvl & LVL_BOOT)
778             out = boot;
779         else
780             out = start;
781
782         mark = false;
783         if (serv->attr.flags & SERV_ALL) {
784             list_for_each(pos, s_start) {
785                 service_t * dep = getservice(pos);
786                 const char * name;
787
788                 if (!dep)
789                     continue;
790
791                 if (dep->attr.flags & SERV_DUPLET)
792                     continue;                   /* Duplet */
793
794 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
795                 if (dep->attr.ref <= 0)
796                     continue;
797 #endif /* not MINIMAL_RULES */
798
799                 /*
800                  * No self dependcies or from the last
801                  */
802                 if (dep == serv || (dep->attr.flags & SERV_ALL))
803                     continue;
804
805                 if ((serv->start->lvl & dep->start->lvl) == 0)
806                     continue;
807
808                 if ((name = dep->attr.script) == (char*)0)
809                     continue;
810
811                 if (!mark) {
812                     fprintf(out, "%s:", target);
813                     mark = true;
814                 }
815                 fprintf(out, " %s", name);
816             }
817         } else {
818             np_list_for_each(pos, &serv->sort.req) {
819                 req_t * req = getreq(pos);
820                 service_t * dep = req->serv;
821                 const char * name;
822
823                 if (!dep)
824                     continue;
825
826                 if (dep->attr.flags & SERV_DUPLET)
827                     continue;
828
829 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
830                 if (dep->attr.ref <= 0)
831                     continue;
832 #endif /* not MINIMAL_RULES */
833
834                 /*
835                  * No self dependcies or from the last
836                  */
837                 if (dep == serv || (dep->attr.flags & SERV_ALL))
838                     continue;
839
840                 if ((serv->start->lvl & dep->start->lvl) == 0)
841                     continue;
842
843                 if ((name = dep->attr.script) == (char*)0)
844                     continue;
845
846                 if (!mark) {
847                     fprintf(out, "%s:", target);
848                     mark = true;
849                 }
850                 fprintf(out, " %s", name);
851             }
852         }
853         if (mark) fputc('\n', out);
854     }
855
856     fclose(boot);
857     fclose(start);
858
859     if (!(stop  = fopen(".depend.stop",  "w"))) {
860         warn("fopen(.depend.stop): %s\n", strerror(errno));
861         return;
862     }
863
864 #ifdef USE_KILL_IN_BOOT
865     if (!(halt = fopen(".depend.halt", "w"))) {
866         warn("fopen(.depend.start): %s\n", strerror(errno));
867         fclose(stop);
868         return;
869     }
870
871     info("creating .depend.halt\n");
872 #endif /* USE_KILL_IN_BOOT */
873     info("creating .depend.stop\n");
874
875     lsort('K');                                 /* Sort into stop order, set new korder */
876
877     target = (char*)0;
878     fprintf(stop, "TARGETS =");
879     while ((serv = listscripts(&target, 'K', LVL_NORM))) {      /* LVL_NORM: nearly all but not BOOT and not SINGLE */
880         if (!serv)
881             continue;
882 #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
883         if (serv->attr.ref <= 0)
884             continue;
885 #endif /* MINIMAL_MAKE */
886         fprintf(stop, " %s", target);
887     }
888     fputc('\n', stop);
889
890 #ifdef USE_KILL_IN_BOOT
891     target = (char*)0;
892     fprintf(halt, "TARGETS =");
893     while ((serv = listscripts(&target, 'K', LVL_BOOT))) {
894         if (!serv)
895             continue;
896 # if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
897         if (serv->attr.ref <= 0)
898             continue;
899 # endif /* MINIMAL_MAKE */
900         fprintf(halt, " %s", target);
901     }
902     fputc('\n', halt);
903 #endif /* USE_KILL_IN_BOOT */
904
905     target = (char*)0;
906     while ((serv = listscripts(&target, 'K', (LVL_NORM|LVL_BOOT)))) {
907         boolean mark;
908         list_t * pos;
909
910         if (!serv)
911             continue;
912 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
913         if (serv->attr.ref <= 0)
914             continue;
915 #endif /* not MINIMAL_RULES */
916
917         if (list_empty(&serv->sort.rev))
918             continue;
919
920         if (serv->stopp->lvl & LVL_BOOT)
921 #ifdef USE_KILL_IN_BOOT
922             out = halt;
923         else
924 #else  /* not USE_KILL_IN_BOOT */
925             continue;
926 #endif /* not USE_KILL_IN_BOOT */
927         out = stop;
928
929         mark = false;
930         np_list_for_each(pos, &serv->sort.rev) {
931             req_t * rev = getreq(pos);
932             service_t * dep = rev->serv;
933             const char * name;
934
935             if (!dep)
936                 continue;
937
938             if (dep->attr.flags & (SERV_DUPLET|SERV_NOSTOP))
939                 continue;                       /* Duplet or no stop link */
940
941 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
942             if (dep->attr.ref <= 0)
943                 continue;
944 #endif /* not MINIMAL_RULES */
945
946             if ((serv->stopp->lvl & dep->stopp->lvl) == 0)
947                 continue;
948
949             if ((name = dep->attr.script) == (char*)0)
950                 continue;
951
952             if (!mark) {
953                 fprintf(out, "%s:", target);
954                 mark = true;
955             }
956             fprintf(out, " %s", name);
957         }
958         if (mark) fputc('\n', out);
959     }
960
961 #ifdef USE_KILL_IN_BOOT
962     fclose(halt);
963 #endif /* USE_KILL_IN_BOOT */
964     fclose(stop);
965 }
966
967 /*
968  * Internal logger
969  */
970 char *myname = (char*)0;
971 static void _logger (const char *restrict const fmt, va_list ap);
972 static void _logger (const char *restrict const fmt, va_list ap)
973 {
974     extension char buf[strlen(myname)+2+strlen(fmt)+1];
975     strcat(strcat(strcpy(buf, myname), ": "), fmt);
976     vfprintf(stderr, buf, ap);
977     return;
978 }
979
980 /*
981  * Cry and exit.
982  */
983 void error (const char *restrict const fmt, ...)
984 {
985     static char called;
986     va_list ap;
987     if (called++)
988         exit (1);
989     va_start(ap, fmt);
990     _logger(fmt, ap);
991     va_end(ap);
992     popd();
993     exit (1);
994 }
995
996 /*
997  * Warn the user.
998  */
999 void warn (const char *restrict const fmt, ...)
1000 {
1001     va_list ap;
1002     va_start(ap, fmt);
1003     _logger(fmt, ap);
1004     va_end(ap);
1005     return;
1006 }
1007
1008 /*
1009  * Print message when verbose is enabled
1010  */
1011 void info(const char *fmt, ...) {
1012     va_list ap;
1013     if (!verbose)
1014         goto out;
1015     va_start(ap, fmt);
1016     _logger(fmt, ap);
1017     va_end(ap);
1018 out:
1019     return;
1020 }
1021
1022 /*
1023  *  Check for script in list.
1024  */
1025 static int curr_argc = -1;
1026 static inline boolean chkfor(const char *restrict const script,
1027                              char **restrict const list, const int cnt) attribute((nonnull(1,2)));
1028 static inline boolean chkfor(const char *restrict const script, char **restrict const list, const int cnt)
1029 {
1030     boolean isinc = false;
1031     register int c = cnt;
1032
1033     curr_argc = -1;
1034     while (c--) {
1035         if (*script != *list[c])
1036             continue;
1037         if (!strcmp(script, list[c])) {
1038             isinc = true;
1039             curr_argc = c;
1040             break;
1041         }
1042     }
1043     return isinc;
1044 }
1045
1046 /*
1047  * Open a runlevel directory, if it not
1048  * exists than create one.
1049  */
1050 static DIR * openrcdir(const char *restrict const rcpath) attribute((nonnull(1)));
1051 static DIR * openrcdir(const char *restrict const rcpath)
1052 {
1053    DIR * rcdir;
1054    struct stat st;
1055    int dfd;
1056
1057     if (stat(rcpath, &st) < 0) {
1058         if (errno == ENOENT) {
1059             info("creating directory '%s'\n", rcpath);
1060             if (!dryrun)
1061                 mkdir(rcpath, (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH));
1062         } else
1063             error("can not stat(%s): %s\n", rcpath, strerror(errno));
1064     }
1065
1066     if ((rcdir = opendir(rcpath)) == (DIR*)0) {
1067         if (dryrun)
1068             warn ("can not opendir(%s): %s\n", rcpath, strerror(errno));
1069         else
1070             error("can not opendir(%s): %s\n", rcpath, strerror(errno));
1071     }
1072 #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
1073     else if ((dfd = dirfd(rcdir)) != 0) {
1074         (void)posix_fadvise(dfd, 0, 0, POSIX_FADV_WILLNEED);
1075         (void)posix_fadvise(dfd, 0, 0, POSIX_FADV_SEQUENTIAL);
1076     }
1077 #endif
1078     return rcdir;
1079 }
1080
1081 /*
1082  * Wrapper for regcomp(3)
1083  */
1084 static inline void regcompiler(regex_t *restrict preg,
1085                                const char *restrict regex,
1086                                int cflags) attribute((always_inline,nonnull(1,2)));
1087 static inline void regcompiler(regex_t *restrict preg, const char *restrict regex, int cflags)
1088 {
1089     register int ret = regcomp(preg, regex, cflags);
1090     if (ret) {
1091         regerror(ret, preg, buf, sizeof (buf));
1092         regfree (preg);
1093         error("%s\n", buf);
1094     }
1095     return;
1096 }
1097
1098 /*
1099  * Wrapper for regexec(3)
1100  */
1101 static inline boolean regexecutor(regex_t *restrict preg,
1102                                   const char *restrict string,
1103                                   size_t nmatch, regmatch_t pmatch[], int eflags) attribute((nonnull(1,2)));
1104 static inline boolean regexecutor(regex_t *preg, const char *string,
1105         size_t nmatch, regmatch_t pmatch[], int eflags)
1106 {
1107     register int ret = regexec(preg, string, nmatch, pmatch, eflags);
1108     if (ret > REG_NOMATCH) {
1109         regerror(ret, preg, buf, sizeof (buf));
1110         regfree (preg);
1111         warn("%s\n", buf);
1112     }
1113     return (ret ? false : true);
1114 }
1115
1116 /*
1117  * The script scanning engine.
1118  * We have to alloc the regular expressions first before
1119  * calling scan_script_defaults().  After the last call
1120  * of scan_script_defaults() we may free the expressions.
1121  */
1122 static inline void scan_script_regalloc(void) attribute((always_inline));
1123 static inline void scan_script_regalloc(void)
1124 {
1125     regcompiler(&reg.prov,      PROVIDES,       REG_EXTENDED|REG_ICASE);
1126     regcompiler(&reg.req_start, REQUIRED_START, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1127     regcompiler(&reg.req_stop,  REQUIRED_STOP,  REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1128     regcompiler(&reg.shl_start, SHOULD_START,   REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1129     regcompiler(&reg.shl_stop,  SHOULD_STOP,    REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1130     regcompiler(&reg.start_bf,  START_BEFORE,   REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1131     regcompiler(&reg.stop_af,   STOP_AFTER,     REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1132     regcompiler(&reg.def_start, DEFAULT_START,  REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1133     regcompiler(&reg.def_stop,  DEFAULT_STOP,   REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1134     regcompiler(&reg.desc,      DESCRIPTION,    REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1135 }
1136
1137 static inline void scan_script_reset(void) attribute((always_inline));
1138 static inline void scan_script_reset(void)
1139 {
1140     xreset(script_inf.provides);
1141     xreset(script_inf.required_start);
1142     xreset(script_inf.required_stop);
1143     xreset(script_inf.should_start);
1144     xreset(script_inf.should_stop);
1145     xreset(script_inf.start_before);
1146     xreset(script_inf.stop_after);
1147     xreset(script_inf.default_start);
1148     xreset(script_inf.default_stop);
1149     xreset(script_inf.description);
1150 }
1151
1152 #define FOUND_LSB_HEADER   0x01
1153 #define FOUND_LSB_DEFAULT  0x02
1154 #define FOUND_LSB_OVERRIDE 0x04
1155
1156 static int o_flags = O_RDONLY;
1157
1158 static uchar scan_lsb_headers(const int dfd, const char *restrict const path,
1159                               const boolean cache, const boolean ignore) attribute((nonnull(2)));
1160 static uchar scan_lsb_headers(const int dfd, const char *restrict const path,
1161                               const boolean cache, const boolean ignore)
1162 {
1163     regmatch_t subloc[SUBNUM_SHD+1], *val = &subloc[SUBNUM-1], *shl = &subloc[SUBNUM_SHD-1];
1164     char *begin = (char*)0, *end = (char*)0;
1165     char *pbuf = buf;
1166     FILE *script;
1167     uchar ret = 0;
1168     int fd;
1169
1170 #define provides        script_inf.provides
1171 #define required_start  script_inf.required_start
1172 #define required_stop   script_inf.required_stop
1173 #define should_start    script_inf.should_start
1174 #define should_stop     script_inf.should_stop
1175 #define start_before    script_inf.start_before
1176 #define stop_after      script_inf.stop_after
1177 #define default_start   script_inf.default_start
1178 #define default_stop    script_inf.default_stop
1179 #define description     script_inf.description
1180
1181     info("Loading %s\n", path);
1182
1183     if ((fd = xopen(dfd, path, o_flags)) < 0 || (script = fdopen(fd, "r")) == (FILE*)0)
1184         error("fopen(%s): %s\n", path, strerror(errno));
1185
1186 #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
1187     (void)posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
1188 #endif
1189
1190 #define COMMON_ARGS     buf, SUBNUM, subloc, 0
1191 #define COMMON_SHD_ARGS buf, SUBNUM_SHD, subloc, 0
1192     while (fgets(buf, sizeof(buf), script)) {
1193
1194         /* Skip scanning above from LSB magic start */
1195         if (!begin) {
1196             if ( (begin = strstr(buf, "### BEGIN INIT INFO")) ) {
1197                 /* Let the latest LSB header override the one found earlier */
1198                 scan_script_reset();
1199             }
1200             continue;
1201         }
1202
1203         if (!provides       && regexecutor(&reg.prov,      COMMON_ARGS) == true) {
1204             if (val->rm_so < val->rm_eo) {
1205                 *(pbuf+val->rm_eo) = '\0';
1206                 provides = xstrdup(pbuf+val->rm_so);
1207             } else
1208                 provides = empty;
1209         }
1210         if (!required_start && regexecutor(&reg.req_start, COMMON_ARGS) == true) {
1211             if (val->rm_so < val->rm_eo) {
1212                 *(pbuf+val->rm_eo) = '\0';
1213                 required_start = xstrdup(pbuf+val->rm_so);
1214             } else
1215                 required_start = empty;
1216         }
1217         if (!required_stop  && regexecutor(&reg.req_stop,  COMMON_ARGS) == true) {
1218             if (val->rm_so < val->rm_eo) {
1219                 *(pbuf+val->rm_eo) = '\0';
1220                 required_stop = xstrdup(pbuf+val->rm_so);
1221             } else
1222                 required_stop = empty;
1223         }
1224         if (!should_start && regexecutor(&reg.shl_start,   COMMON_SHD_ARGS) == true) {
1225             if (shl->rm_so < shl->rm_eo) {
1226                 *(pbuf+shl->rm_eo) = '\0';
1227                 should_start = xstrdup(pbuf+shl->rm_so);
1228             } else
1229                 should_start = empty;
1230         }
1231         if (!should_stop  && regexecutor(&reg.shl_stop,    COMMON_SHD_ARGS) == true) {
1232             if (shl->rm_so < shl->rm_eo) {
1233                 *(pbuf+shl->rm_eo) = '\0';
1234                 should_stop = xstrdup(pbuf+shl->rm_so);
1235             } else
1236                 should_stop = empty;
1237         }
1238         if (!start_before && regexecutor(&reg.start_bf,    COMMON_SHD_ARGS) == true) {
1239             if (shl->rm_so < shl->rm_eo) {
1240                 *(pbuf+shl->rm_eo) = '\0';
1241                 start_before = xstrdup(pbuf+shl->rm_so);
1242             } else
1243                 start_before = empty;
1244         }
1245         if (!stop_after  && regexecutor(&reg.stop_af,      COMMON_SHD_ARGS) == true) {
1246             if (shl->rm_so < shl->rm_eo) {
1247                 *(pbuf+shl->rm_eo) = '\0';
1248                 stop_after = xstrdup(pbuf+shl->rm_so);
1249             } else
1250                 stop_after = empty;
1251         }
1252         if (!default_start  && regexecutor(&reg.def_start, COMMON_ARGS) == true) {
1253             if (val->rm_so < val->rm_eo) {
1254                 *(pbuf+val->rm_eo) = '\0';
1255                 default_start = xstrdup(pbuf+val->rm_so);
1256             } else
1257                 default_start = empty;
1258         }
1259 #ifndef SUSE
1260         if (!default_stop   && regexecutor(&reg.def_stop,  COMMON_ARGS) == true) {
1261             if (val->rm_so < val->rm_eo) {
1262                 *(pbuf+val->rm_eo) = '\0';
1263                 default_stop = xstrdup(pbuf+val->rm_so);
1264             } else
1265                 default_stop = empty;
1266         }
1267 #endif
1268         if (!description    && regexecutor(&reg.desc,      COMMON_ARGS) == true) {
1269             if (val->rm_so < val->rm_eo) {
1270                 *(pbuf+val->rm_eo) = '\0';
1271                 description = xstrdup(pbuf+val->rm_so);
1272             } else
1273                 description = empty;
1274         }
1275
1276         /* Skip scanning below from LSB magic end */
1277         if ((end = strstr(buf, "### END INIT INFO")))
1278             break;
1279     }
1280 #undef COMMON_ARGS
1281 #undef COMMON_SHD_ARGS
1282
1283 #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
1284     if (cache) {
1285         off_t deep = ftello(script);
1286         (void)posix_fadvise(fd, 0, deep, POSIX_FADV_WILLNEED);
1287         (void)posix_fadvise(fd, deep, 0, POSIX_FADV_DONTNEED);
1288     } else
1289         (void)posix_fadvise(fd, 0, 0, POSIX_FADV_NOREUSE);
1290 #endif
1291
1292     fclose(script);
1293
1294     if (begin && end)
1295         ret |= FOUND_LSB_HEADER;
1296
1297     if (begin && !end) {
1298         char *name = basename(path);
1299         if (*name == 'S' || *name == 'K')
1300             name += 3;
1301         warn("Script %s is broken: missing end of LSB comment.\n", name);
1302         if (!ignore)
1303             error("exiting now!\n");
1304     }
1305
1306     if (begin && end && (!provides || (provides == empty) ||
1307 #ifdef SUSE
1308                          !required_start || !required_stop || !default_start
1309 #else  /* not SUSE */
1310                          !required_start || !required_stop || !default_start || !default_stop
1311 #endif /* not SUSE */
1312         ))
1313     {
1314         char *name = basename(path);
1315         if (*name == 'S' || *name == 'K')
1316             name += 3;
1317         warn("Script %s is broken: incomplete LSB comment.\n", name);
1318         if (!provides)
1319             warn("missing `Provides:' entry: please add.\n");
1320         if (provides == empty)
1321             warn("missing valid name for `Provides:' please add.\n");
1322         if (!required_start)
1323             warn("missing `Required-Start:' entry: please add even if empty.\n");
1324         if (!required_stop)
1325             warn("missing `Required-Stop:'  entry: please add even if empty.\n");
1326         if (!default_start)
1327             warn("missing `Default-Start:'  entry: please add even if empty.\n");
1328 #ifndef SUSE
1329         if (!default_stop)
1330             warn("missing `Default-Stop:'   entry: please add even if empty.\n");
1331 #endif
1332     }
1333
1334 #undef provides
1335 #undef required_start
1336 #undef required_stop
1337 #undef should_start
1338 #undef should_stop
1339 #undef start_before
1340 #undef stop_after
1341 #undef default_start
1342 #undef default_stop
1343 #undef description
1344     return ret;
1345 }
1346
1347 /*
1348  * Follow symlinks, return the basename of the file pointed to by
1349  * symlinks or the basename of the current path if no symlink.
1350  */
1351 static char * scriptname(int dfd, const char *restrict const path, char **restrict first) attribute((malloc,nonnull(2)));
1352 static char * scriptname(int dfd, const char *restrict const path, char **restrict first)
1353 {
1354     uint deep = 0;
1355     char linkbuf[PATH_MAX+1];
1356     char *script = xstrdup(path);
1357
1358     strncpy(linkbuf, script, sizeof(linkbuf)-1);
1359     linkbuf[PATH_MAX] = '\0';
1360
1361     do {
1362         struct stat st;
1363         int linklen;
1364
1365         if (deep++ > MAXSYMLINKS) {
1366             errno = ELOOP;
1367             warn("Can not determine script name for %s: %s\n", path, strerror(errno));
1368             break;
1369         }
1370
1371         if (xlstat(dfd, script, &st) < 0) {
1372             warn("Can not stat %s: %s\n", script, strerror(errno));
1373             break;
1374         }
1375
1376         if (!S_ISLNK(st.st_mode))
1377             break;
1378
1379         if ((linklen = xreadlink(dfd, script, linkbuf, sizeof(linkbuf)-1)) < 0)
1380             break;
1381         linkbuf[linklen] = '\0';
1382
1383         if (linkbuf[0] != '/') {        /* restore relative links */
1384             const char *lastslash;
1385
1386             if ((lastslash = strrchr(script, '/'))) {
1387                 size_t dirname_len = lastslash - script + 1;
1388
1389                 if (dirname_len + linklen > PATH_MAX)
1390                     linklen = PATH_MAX - dirname_len;
1391
1392                 memmove(&linkbuf[dirname_len], &linkbuf[0], linklen + 1);
1393                 memcpy(&linkbuf[0], script, dirname_len);
1394             }
1395         }
1396
1397         free(script);
1398         script = xstrdup(linkbuf);
1399
1400         if (deep == 1 && first)
1401             *first = xstrdup(basename(linkbuf));
1402
1403     } while (1);
1404
1405     free(script);
1406     script = xstrdup(basename(linkbuf));
1407
1408     return script;
1409 }
1410
1411 static uchar load_overrides(const char *restrict const dir,
1412                             const char *restrict const name,
1413                             const boolean cache, const boolean ignore) attribute((nonnull(1,2)));
1414 static uchar load_overrides(const char *restrict const dir,
1415                             const char *restrict const name,
1416                             const boolean cache, const boolean ignore)
1417 {
1418     uchar ret = 0;
1419     char fullpath[PATH_MAX+1];
1420     struct stat statbuf;
1421     int n;
1422
1423     n = snprintf(&fullpath[0], sizeof(fullpath), "%s%s/%s", (root && !set_override) ? root : "", dir, name);
1424     if (n >= (int)sizeof(fullpath) || n < 0)
1425         error("snprintf(): %s\n", strerror(errno));
1426
1427     if (stat(fullpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
1428         ret = scan_lsb_headers(-1, fullpath, cache, ignore);
1429     if (ret & FOUND_LSB_HEADER)
1430         ret |= FOUND_LSB_OVERRIDE;
1431     return ret;
1432 }
1433
1434 static uchar scan_script_defaults(int dfd, const char *const restrict path,
1435                                   const char *const restrict override_path,
1436                                   char **restrict first,
1437                                   const boolean cache, const boolean ignore) attribute((nonnull(2,3)));
1438 static uchar scan_script_defaults(int dfd, const char *restrict const path,
1439                                   const char *restrict const override_path,
1440                                   char **restrict first,
1441                                   const boolean cache, const boolean ignore)
1442 {
1443     char * name = scriptname(dfd, path, first);
1444     uchar ret = 0;
1445
1446     if (!name)
1447         return ret;
1448
1449     /* Reset old results */
1450     scan_script_reset();
1451
1452 #ifdef SUSE
1453     /* Common script ... */
1454     if (!strcmp(name, "halt")) {
1455         ret |= (FOUND_LSB_HEADER|FOUND_LSB_DEFAULT);
1456         goto out;
1457     }
1458
1459     /* ... and its link */
1460     if (!strcmp(name, "reboot")) {
1461         ret |= (FOUND_LSB_HEADER|FOUND_LSB_DEFAULT);
1462         goto out;
1463     }
1464
1465     /* Common script for single mode */
1466     if (!strcmp(name, "single")) {
1467         ret |= (FOUND_LSB_HEADER|FOUND_LSB_DEFAULT);
1468         goto out;
1469     }
1470 #endif /* SUSE */
1471
1472     /* Replace with headers from the script itself */
1473     ret |= scan_lsb_headers(dfd, path, cache, ignore);
1474
1475     /* Load values if the override file exist */
1476     if ((ret & FOUND_LSB_HEADER) == 0)
1477         ret |= load_overrides("/usr/share/insserv/overrides", name, cache, ignore);
1478     else
1479         ret |= FOUND_LSB_DEFAULT;
1480
1481     /*
1482      * Allow host-specific overrides to replace the content in the
1483      * init.d scripts
1484      */
1485     ret |= load_overrides(override_path, name, cache, ignore);
1486 #ifdef SUSE
1487 out:
1488 #endif /* SUSE */
1489     free(name);
1490     return ret;
1491 }
1492
1493 static inline void scan_script_regfree() attribute((always_inline));
1494 static inline void scan_script_regfree()
1495 {
1496     regfree(&reg.prov);
1497     regfree(&reg.req_start);
1498     regfree(&reg.req_stop);
1499     regfree(&reg.shl_start);
1500     regfree(&reg.shl_stop);
1501     regfree(&reg.start_bf);
1502     regfree(&reg.stop_af);
1503     regfree(&reg.def_start);
1504     regfree(&reg.def_stop);
1505     regfree(&reg.desc);
1506 }
1507
1508 static struct {
1509     char *location;
1510     const ushort lvl;
1511     const ushort seek;
1512     const char key;
1513 } attribute((aligned(sizeof(char*)))) runlevel_locations[] = {
1514 #ifdef SUSE     /* SuSE's SystemV link scheme */
1515     {"rc0.d/",    LVL_HALT,   LVL_NORM, '0'},
1516     {"rc1.d/",    LVL_ONE,    LVL_NORM, '1'}, /* runlevel 1 switch over to single user mode */
1517     {"rc2.d/",    LVL_TWO,    LVL_NORM, '2'},
1518     {"rc3.d/",    LVL_THREE,  LVL_NORM, '3'},
1519     {"rc4.d/",    LVL_FOUR,   LVL_NORM, '4'},
1520     {"rc5.d/",    LVL_FIVE,   LVL_NORM, '5'},
1521     {"rc6.d/",    LVL_REBOOT, LVL_NORM, '6'},
1522     {"rcS.d/",    LVL_SINGLE, LVL_NORM, 'S'}, /* runlevel S is for single user mode */
1523     {"boot.d/",   LVL_BOOT,   LVL_BOOT, 'B'}, /* runlevel B is for system initialization */
1524 #else           /* not SUSE (actually, Debian) */
1525     {"../rc0.d/", LVL_HALT,   LVL_NORM, '0'},
1526     {"../rc1.d/", LVL_ONE,    LVL_NORM, '1'}, /* runlevel 1 switch over to single user mode */
1527     {"../rc2.d/", LVL_TWO,    LVL_NORM, '2'},
1528     {"../rc3.d/", LVL_THREE,  LVL_NORM, '3'},
1529     {"../rc4.d/", LVL_FOUR,   LVL_NORM, '4'},
1530     {"../rc5.d/", LVL_FIVE,   LVL_NORM, '5'},
1531     {"../rc6.d/", LVL_REBOOT, LVL_NORM, '6'},
1532     {"../rcS.d/", LVL_BOOT,   LVL_BOOT, 'S'}, /* runlevel S is for system initialization */
1533                 /* On e.g. Debian there exist no boot.d */
1534 #endif          /* not SUSE */
1535 };
1536
1537 #define RUNLEVLES (int)(sizeof(runlevel_locations)/sizeof(runlevel_locations[0]))
1538
1539 int map_has_runlevels(void)
1540 {
1541     return RUNLEVLES;
1542 }
1543
1544 char map_runlevel_to_key(const int runlevel)
1545 {
1546     if (runlevel >= RUNLEVLES) {
1547         warn("Wrong runlevel %d\n", runlevel);
1548     }
1549     return runlevel_locations[runlevel].key;
1550 }
1551
1552 ushort map_key_to_lvl(const char key)
1553 {
1554     int runlevel;
1555     const char uckey = toupper(key);
1556     for (runlevel = 0; runlevel < RUNLEVLES; runlevel++) {
1557         if (uckey == runlevel_locations[runlevel].key)
1558             return runlevel_locations[runlevel].lvl;
1559     }
1560     warn("Wrong runlevel key '%c'\n", uckey);
1561     return 0;
1562 }
1563
1564 const char *map_runlevel_to_location(const int runlevel)
1565 {
1566     if (runlevel >= RUNLEVLES) {
1567         warn("Wrong runlevel %d\n", runlevel);
1568     }
1569     return runlevel_locations[runlevel].location;
1570 }
1571
1572 ushort map_runlevel_to_lvl(const int runlevel)
1573 {
1574     if (runlevel >= RUNLEVLES) {
1575         warn("Wrong runlevel %d\n", runlevel);
1576     }
1577     return runlevel_locations[runlevel].lvl;
1578 }
1579
1580 ushort map_runlevel_to_seek(const int runlevel)
1581 {
1582     return runlevel_locations[runlevel].seek;
1583 }
1584
1585 /*
1586  * Two helpers for runlevel bits and strings.
1587  */
1588 ushort str2lvl(const char *restrict lvl)
1589 {
1590     char * token, *tmp = strdupa(lvl);
1591     ushort ret = 0;
1592
1593     if (!tmp)
1594         error("%s", strerror(errno));
1595
1596     while ((token = strsep(&tmp, delimeter))) {
1597         if (!*token || strlen(token) != 1)
1598             continue;
1599         if (!strpbrk(token, "0123456sSbB"))
1600             continue;
1601
1602         ret |= map_key_to_lvl(*token);
1603     }
1604
1605     return ret;
1606 }
1607
1608 char * lvl2str(const ushort lvl)
1609 {
1610     char * ptr, * last;
1611     char str[20];
1612     int num;
1613     uint bit = 0x001;
1614
1615     last = ptr = &str[0];
1616     memset(ptr, '\0', sizeof(str));
1617     for (num = 0; num < RUNLEVLES; num++) {
1618         if (bit & lvl) {
1619             if (ptr > last)
1620                 *ptr++ = ' ';
1621             last = ptr;
1622             if (LVL_NORM & bit)
1623                 *ptr++ = num + 48;
1624 #ifdef SUSE
1625             else if (LVL_SINGLE & bit)
1626                 *ptr++ = 'S';
1627             else if (LVL_BOOT & bit)
1628                 *ptr++ = 'B';
1629 #else  /* not SUSE */
1630             else if (LVL_BOOT & bit)
1631                 *ptr++ = 'S';
1632 #endif /* not SUSE */
1633             else
1634                 error("Wrong runlevel %d\n", num);
1635         }
1636         bit <<= 1;
1637     }
1638     if (strlen(str) == 0)
1639         return (char*)0;
1640     return xstrdup(str);
1641 }
1642
1643 /*
1644  * Scan current service structure
1645  */
1646 static void scan_script_locations(const char *const restrict path,
1647                                   const char *const restrict override_path,
1648                                   const boolean ignore) attribute((nonnull(1,2)));
1649 static void scan_script_locations(const char *const path, const char *const override_path,
1650                                   const boolean ignore)
1651 {
1652     int runlevel;
1653
1654     pushd(path);
1655     for (runlevel = 0; runlevel < RUNLEVLES; runlevel++) {
1656         const char * rcd = (char*)0;
1657         struct stat st_script;
1658         struct dirent *d;
1659         DIR  * rcdir;
1660         char * token;
1661         int dfd;
1662
1663         rcd = map_runlevel_to_location(runlevel);
1664
1665         rcdir = openrcdir(rcd);         /* Creates runlevel directory if necessary */
1666         if (rcdir == (DIR*)0)
1667             break;
1668         if ((dfd = dirfd(rcdir)) < 0) {
1669             closedir(rcdir);
1670             break;
1671         }
1672         pushd(rcd);
1673
1674         while ((d = readdir(rcdir)) != (struct dirent*)0) {
1675             char * name = (char *)0;
1676             char * ptr = d->d_name;
1677             service_t * first;
1678             char * begin;               /* Remember address of ptr handled by strsep() */
1679             char order;
1680             uchar lsb;
1681             char type;
1682
1683             if (*ptr != 'S' && *ptr != 'K')
1684                 continue;
1685             type = *ptr;
1686             ptr++;
1687
1688             if (strspn(ptr, "0123456789") < 2)
1689                 continue;
1690             order = atoi(ptr);
1691             ptr += 2;
1692
1693             if (xstat(dfd, d->d_name, &st_script) < 0) {
1694                 xremove(dfd, d->d_name);        /* dangling sym link */
1695                 continue;
1696             }
1697
1698             lsb = scan_script_defaults(dfd, d->d_name, override_path, &name, true, ignore);
1699             if (!script_inf.provides || script_inf.provides == empty)
1700                 script_inf.provides = xstrdup(ptr);
1701
1702 #ifndef SUSE
1703             if (!lsb) {
1704                 script_inf.required_start = xstrdup(DEFAULT_DEPENDENCY);
1705                 script_inf.required_stop  = xstrdup(DEFAULT_DEPENDENCY);
1706             }
1707 #endif /* not SUSE */
1708
1709             first = (service_t*)0;
1710             begin = script_inf.provides;
1711             while ((token = strsep(&begin, delimeter)) && *token) {
1712                 service_t * service;
1713
1714                 if (*token == '$') {
1715                     warn("script %s provides system facility %s, skipped!\n", d->d_name, token);
1716                     continue;
1717                 }
1718                 if (*token == '#') {
1719                     warn("script %s provides facility %s with comment sign, skipped!\n", d->d_name, token);
1720                     continue;
1721                 }
1722
1723                 service = current_structure(token, order, runlevel, type);
1724
1725                 if (first)
1726                     nickservice(first, service);
1727                 else
1728                     first = service;
1729
1730                 if (!makeprov(service, name))
1731                     continue;
1732
1733                 ++service->attr.ref;                    /* May enabled in several levels */
1734
1735                 if (service->attr.flags & SERV_KNOWN)
1736                     continue;
1737                 service->attr.flags |= (SERV_KNOWN|SERV_ENABLED);
1738
1739                 if (!lsb)
1740                     service->attr.flags |= SERV_NOTLSB;
1741
1742                 if ((lsb & FOUND_LSB_HEADER) == 0) {
1743                     if ((lsb & (FOUND_LSB_DEFAULT|FOUND_LSB_OVERRIDE)) == 0)
1744                       warn("warning: script '%s' missing LSB tags and overrides\n", d->d_name);
1745                     else
1746                         warn("warning: script '%s' missing LSB tags\n", d->d_name);
1747                 }
1748
1749                 if (script_inf.required_start && script_inf.required_start != empty) {
1750                     rememberreq(service, REQ_MUST, script_inf.required_start);
1751 #ifdef USE_COMPAT_EMPTY
1752                     if (!script_inf.required_stop || script_inf.required_stop == empty)
1753                         script_inf.required_stop = xstrdup(script_inf.required_start);
1754 #endif /* USE_COMPAT_EMPTY */
1755                 }
1756                 if (script_inf.should_start && script_inf.should_start != empty) {
1757                     rememberreq(service, REQ_SHLD, script_inf.should_start);
1758 #ifdef USE_COMPAT_EMPTY
1759                     if (!script_inf.should_stop || script_inf.should_stop == empty)
1760                         script_inf.should_stop = xstrdup(script_inf.should_start);
1761 #endif /* USE_COMPAT_EMPTY */
1762                 }
1763                 if (script_inf.start_before && script_inf.start_before != empty) {
1764                     reversereq(service, REQ_SHLD, script_inf.start_before);
1765 #ifdef USE_COMPAT_EMPTY
1766                     if (!script_inf.stop_after || script_inf.stop_after == empty)
1767                         script_inf.stop_after = xstrdup(script_inf.start_before);
1768 #endif /* USE_COMPAT_EMPTY */
1769                 }
1770                 if (script_inf.required_stop && script_inf.required_stop != empty) {
1771                     rememberreq(service, REQ_MUST|REQ_KILL, script_inf.required_stop);
1772                 }
1773                 if (script_inf.should_stop && script_inf.should_stop != empty) {
1774                     rememberreq(service, REQ_SHLD|REQ_KILL, script_inf.should_stop);
1775                 }
1776                 if (script_inf.stop_after && script_inf.stop_after != empty) {
1777                     reversereq(service, REQ_SHLD|REQ_KILL, script_inf.stop_after);
1778                 }
1779             }
1780
1781             if (name) 
1782                 xreset(name);
1783
1784             scan_script_reset();
1785
1786         }       /* while ((token = strsep(&begin, delimeter)) && *token) */
1787
1788         popd();
1789         closedir(rcdir);
1790     }
1791     popd();
1792     return;
1793 }
1794
1795 /*
1796  * The /etc/insserv.conf scanning engine.
1797  */
1798 static void scan_conf_file(const char *restrict file) attribute((nonnull(1)));
1799 static void scan_conf_file(const char *restrict file)
1800 {
1801     regmatch_t subloc[SUBCONFNUM], *val = (regmatch_t*)0;
1802     FILE *conf;
1803
1804     info("Loading %s\n", file);
1805
1806     do {
1807         const char * fptr = file;
1808         if (*fptr == '/')
1809             fptr++;
1810         /* Try relativ location first */
1811         if ((conf = fopen(fptr, "r")))
1812             break;
1813         /* Try absolute location */
1814         if ((conf = fopen(file, "r")))
1815             break;
1816         goto err;
1817     } while (1);
1818
1819     while (fgets(buf, sizeof(buf), conf)) {
1820         char *pbuf = &buf[0];
1821         if (*pbuf == '#')
1822             continue;
1823         if (*pbuf == '\n')
1824             continue;
1825         if (regexecutor(&creg.isysfaci, buf, SUBCONFNUM, subloc, 0) == true) {
1826             char * virt = (char*)0, * real = (char*)0;
1827             val = &subloc[SUBCONF - 1];
1828             if (val->rm_so < val->rm_eo) {
1829                 *(pbuf+val->rm_eo) = '\0';
1830                 virt = pbuf+val->rm_so;
1831             }
1832             val = &subloc[SUBCONFNUM - 1];
1833             if (val->rm_so < val->rm_eo) {
1834                 *(pbuf+val->rm_eo) = '\0';
1835                 real = pbuf+val->rm_so;
1836             }
1837             if (virt) {
1838                 list_t * ptr;
1839                 boolean found = false;
1840                 list_for_each(ptr, sysfaci_start) {
1841                     if (!strcmp(getfaci(ptr)->name, virt)) {
1842                         found = true;
1843                         if(real) {
1844                             list_t * r_list = &getfaci(ptr)->replace;
1845                             char * token;
1846                             while ((token = strsep(&real, delimeter))) {
1847                                 repl_t *restrict subst;
1848                                 string_t * r;
1849                                 if (posix_memalign((void*)&subst, sizeof(void*), alignof(repl_t)) != 0)
1850                                     error("%s", strerror(errno));
1851                                 insert(&subst->r_list, r_list->prev);
1852                                 r = &subst->r[0];
1853                                 if (posix_memalign((void*)&r->ref, sizeof(void*), alignof(typeof(r->ref))+strsize(token)) != 0)
1854                                     error("%s", strerror(errno));
1855                                 *r->ref = 1;
1856                                 r->name = ((char*)(r->ref))+alignof(typeof(r->ref));
1857                                 strcpy(r->name, token);
1858                             }
1859                         }
1860                         break;
1861                     }
1862                 }
1863                 if (!found) {
1864                     faci_t *restrict this;
1865                     if (posix_memalign((void*)&this, sizeof(void*), alignof(faci_t)) != 0)
1866                         error("%s", strerror(errno));
1867                     else {
1868                         list_t * r_list = &this->replace;
1869                         char * token;
1870                         r_list->next = r_list;
1871                         r_list->prev = r_list;
1872                         insert(&this->list, sysfaci_start->prev);
1873                         this->name = xstrdup(virt);
1874                         while ((token = strsep(&real, delimeter))) {
1875                             repl_t *restrict subst;
1876                             string_t * r;
1877                             if (posix_memalign((void*)&subst, sizeof(void*), alignof(repl_t)) != 0)
1878                                 error("%s", strerror(errno));
1879                             insert(&subst->r_list, r_list->prev);
1880                             r = &subst->r[0];
1881                             if (posix_memalign((void*)&r->ref, sizeof(void*), alignof(typeof(r->ref))+strsize(token)) != 0)
1882                                 error("%s", strerror(errno));
1883                             *r->ref = 1;
1884                             r->name = ((char*)(r->ref))+alignof(typeof(r->ref));
1885                             strcpy(r->name, token);
1886                         }
1887                     }
1888                 }
1889             }
1890         }
1891         if (regexecutor(&creg.isactive, buf, SUBCONFNUM, subloc, 0) == true) {
1892             char * key = (char*)0, * servs = (char*)0;
1893             val = &subloc[SUBCONF - 1];
1894             if (val->rm_so < val->rm_eo) {
1895                 *(pbuf+val->rm_eo) = '\0';
1896                 key = pbuf+val->rm_so;
1897             }
1898             val = &subloc[SUBCONFNUM - 1];
1899             if (val->rm_so < val->rm_eo) {
1900                 *(pbuf+val->rm_eo) = '\0';
1901                 servs = pbuf+val->rm_so;
1902             }
1903             if (key && *key == '<' && servs && *servs) {
1904                 if (!strncmp("<interactive>", key, strlen(key))) {
1905                     char * token;
1906                     while ((token = strsep(&servs, delimeter))) {
1907                         service_t *service = addservice(token);
1908                         service = getorig(service);
1909                         service->attr.flags |= SERV_INTRACT;
1910                     }
1911                 }
1912             }
1913         }
1914     }
1915
1916     fclose(conf);
1917     return;
1918 err:
1919     warn("fopen(%s): %s\n", file, strerror(errno));
1920 }
1921
1922 static int cfgfile_filter(const struct dirent *restrict d) attribute((nonnull(1)));
1923 static int cfgfile_filter(const struct dirent *restrict d)
1924 {
1925     boolean ret = false;
1926     const char * name = d->d_name;
1927     const char * end;
1928
1929     if (*name == '.')
1930         goto out;
1931     if (!name || (*name == '\0'))
1932         goto out;
1933     if ((end = strrchr(name, '.'))) {
1934         end++;
1935         if (!strncmp(end, "rpm", 3)     || /* .rpmorig, .rpmnew, .rmpsave, ... */
1936             !strncmp(end, "ba", 2)      || /* .bak, .backup, ... */
1937 #ifdef SUSE
1938             !strcmp(end,  "local")      || /* .local are sourced by the basename */
1939 #endif /* not SUSE */
1940             !strcmp(end,  "old")        ||
1941             !strcmp(end,  "new")        ||
1942             !strcmp(end,  "org")        ||
1943             !strcmp(end,  "orig")       ||
1944             !strncmp(end, "dpkg", 3)    || /* .dpkg-old, .dpkg-new ... */
1945             !strcmp(end,  "save")       ||
1946             !strcmp(end,  "swp")        || /* Used by vi like editors */
1947             !strcmp(end,  "core"))         /* modern core dump */
1948         {
1949             goto out;
1950         }
1951     }
1952     if ((end = strrchr(name, ','))) {
1953         end++;
1954         if (!strcmp(end,  "v"))           /* rcs-files */
1955             goto out;
1956     }
1957     ret = true;
1958 out:
1959     return (int)ret;
1960 }
1961
1962 static void scan_conf(const char *restrict file) attribute((nonnull(1)));
1963 static void scan_conf(const char *restrict file)
1964 {
1965     struct dirent** namelist = (struct dirent**)0;
1966     char path[PATH_MAX+1];
1967     int n;
1968
1969     regcompiler(&creg.isysfaci,  CONFLINE, REG_EXTENDED|REG_ICASE);
1970     regcompiler(&creg.isactive, CONFLINE2, REG_EXTENDED|REG_ICASE);
1971
1972     n = snprintf(&path[0], sizeof(path), "%s%s",   (root && !set_insconf) ? root : "", file);
1973     if (n >= (int)sizeof(path) || n < 0)
1974         error("snprintf(): %s\n", strerror(errno));
1975
1976     scan_conf_file(path);
1977
1978     n = snprintf(&path[0], sizeof(path), "%s%s.d", (root && !set_insconf) ? root : "", file);
1979     if (n >= (int)sizeof(path) || n < 0)
1980         error("snprintf(): %s\n", strerror(errno));
1981
1982     n = scandir(path, &namelist, cfgfile_filter, alphasort);
1983     if(n > 0) {
1984         while(n--) {
1985             struct stat st;
1986             char buf[PATH_MAX+1];
1987             int r;
1988
1989             r = snprintf(&buf[0], sizeof(buf), "%s/%s", path, namelist[n]->d_name);
1990             if (r >= (int)sizeof(buf) || r < 0)
1991                 error("snprintf(): %s\n", strerror(errno));
1992
1993             if ((stat(buf, &st) < 0) || !S_ISREG(st.st_mode))
1994                 continue;
1995
1996             scan_conf_file(buf);
1997
1998             free(namelist[n]);
1999         }
2000     }
2001
2002     if (namelist)
2003         free(namelist);
2004
2005     regfree(&creg.isysfaci);
2006     regfree(&creg.isactive);
2007 }
2008
2009 static void expand_faci(list_t *restrict rlist, list_t *restrict head,
2010                         int *restrict deep) attribute((noinline,nonnull(1,2,3)));
2011 static void expand_faci(list_t *restrict rlist, list_t *restrict head, int *restrict deep)
2012 {
2013         repl_t * rent = getrepl(rlist);
2014         list_t * tmp, * safe, * ptr = (list_t*)0;
2015
2016         list_for_each(tmp, sysfaci_start) {
2017             if (!strcmp(getfaci(tmp)->name, rent->r[0].name)) {
2018                 ptr = &getfaci(tmp)->replace;
2019                 break;
2020             }
2021         }
2022
2023         if (!ptr || list_empty(ptr)) {
2024             delete(rlist);
2025             if (--(*rent->r[0].ref) <= 0)
2026                 free(rent->r[0].ref);
2027             free(rent);
2028             goto out;
2029         }
2030
2031         if ((*deep)++ > 10) {
2032             warn("The nested level of the system facilities in the insserv.conf file(s) is to large\n");
2033             goto out;
2034         }
2035
2036         list_for_each_safe(tmp, safe, ptr) {
2037             repl_t * rnxt = getrepl(tmp);
2038             if (*rnxt->r[0].name == '$') {
2039                 expand_faci(tmp, head, deep);
2040             } else {
2041                 if (*deep == 1) {
2042                     if (--(*rent->r[0].ref) <= 0)
2043                         free(rent->r[0].ref);
2044                     rent->r[0] = rnxt->r[0];
2045                     ++(*rent->r[0].ref);
2046                 } else {
2047                     repl_t *restrict subst;
2048                     if (posix_memalign((void*)&subst, sizeof(void*), alignof(repl_t)) != 0)
2049                         error("%s", strerror(errno));
2050                     insert(&subst->r_list, head);
2051                     subst->r[0] = rnxt->r[0];
2052                     ++(*subst->r[0].ref);
2053                 }
2054             }
2055         }
2056 out:
2057         (*deep)--;
2058         return;
2059 }
2060
2061 static inline void expand_conf(void)
2062 {
2063     list_t *ptr;
2064     list_for_each(ptr, sysfaci_start) {
2065         list_t * rlist, * safe, * head = &getfaci(ptr)->replace;
2066         list_for_each_safe(rlist, safe, head) {
2067             if (*getrepl(rlist)->r[0].name == '$') {
2068                 int deep = 0;
2069                 expand_faci(rlist, rlist, &deep);
2070             }
2071         }
2072     }
2073 }
2074
2075 /*
2076  * Scan for a Start or Kill script within a runlevel directory.
2077  * We start were we leave the directory, the upper level
2078  * has to call rewinddir(3) if necessary.
2079  */
2080 static inline char * scan_for(DIR *const restrict rcdir,
2081                               const char *const restrict script,
2082                               const char type) attribute((always_inline,nonnull(1,2)));
2083 static inline char * scan_for(DIR *const rcdir,
2084                               const char *const script, const char type)
2085 {
2086     struct dirent *d;
2087     char * ret = (char*)0;
2088
2089     while ((d = readdir(rcdir)) != (struct dirent*)0) {
2090         char * ptr = d->d_name;
2091
2092         if (*ptr != type)
2093             continue;
2094         ptr++;
2095
2096         if (strspn(ptr, "0123456789") < 2)
2097             continue;
2098         ptr += 2;
2099
2100         if (!strcmp(ptr, script)) {
2101             ret = d->d_name;
2102             break;
2103         }
2104     }
2105     return ret;
2106 }
2107
2108 #ifdef SUSE
2109 /*
2110  * A simple command line checker of the parent process to determine if this is
2111  * a sub process "/bin/sh" forked off for executing a temporary file for %preun,
2112  * %postun, %pre, or %post scriptlet.
2113  */
2114 static inline boolean underrpm(void)
2115 {
2116     boolean ret = false;
2117     const pid_t pp = getppid();
2118     char buf[PATH_MAX], *argv[3], *ptr;
2119 # if defined(USE_RPMLIB) && (USE_RPMLIB > 0)
2120     char *tmppath, *shell;
2121 # endif /* USE_RPMLIB */
2122     int argc, fd;
2123     ssize_t len;
2124
2125     snprintf(buf, sizeof(buf)-1, "/proc/%lu/cmdline", (unsigned long)pp);
2126     if ((fd = open(buf, O_NOCTTY|O_RDONLY)) < 0)
2127         goto out;
2128
2129     memset(buf, '\0', sizeof(buf));
2130     if ((len = read(fd , buf, sizeof(buf)-1)) < 0)
2131         goto out;
2132
2133     ptr = &buf[0];
2134     argc = 0;
2135     do {
2136         argv[argc++] = ptr;
2137         if (argc > 2)
2138             break;
2139         if ((len = len - (ssize_t)(ptr - &buf[0])) < 0)
2140             break;
2141     } while ((ptr = memchr(ptr, '\0', len)) && *(++ptr));
2142
2143     if (argc != 3)
2144         goto out;
2145
2146 # if defined(USE_RPMLIB) && (USE_RPMLIB > 0)
2147     rpmReadConfigFiles(NULL, NULL);
2148     rpmFreeRpmrc();
2149
2150     if ((shell = rpmExpand("%_buildshell", NULL)) == NULL)
2151         shell = xstrdup("/bin/sh");
2152
2153     if (strncmp(argv[0], shell, strlen(shell)) != 0) {
2154         free(shell);
2155         goto out;
2156     }
2157     free(shell);
2158
2159     if ((tmppath = rpmExpand("%_tmppath", NULL)) == NULL)
2160         tmppath = xstrdup("/var/tmp");
2161
2162     if (strncmp(argv[1], tmppath, strlen(tmppath)) != 0) {
2163         free(tmppath);
2164         goto out;
2165     }
2166
2167     len = strlen(tmppath);
2168     free(tmppath);
2169
2170     ptr = argv[1];
2171     if (strncmp(ptr + len, "/rpm-tmp.", 9) != 0)
2172         goto out;
2173 # else  /* not USE_RPMLIB */
2174     if ((strcmp(argv[0], "/bin/sh") != 0) &&
2175         (strcmp(argv[0], "/bin/bash") != 0))
2176         goto out;
2177
2178     if ((strncmp(argv[1], "/var/tmp/rpm-tmp.", 17) != 0) &&
2179         (strncmp(argv[1], "/usr/tmp/rpm-tmp.", 17) != 0) &&
2180         (strncmp(argv[1], "/tmp/rpm-tmp.", 13) != 0))
2181         goto out;
2182 # endif /* not USE_RPMLIB */
2183     if ((argc = atoi(argv[2])) >= 0 && argc <= 2)
2184         ret = true;
2185 out:
2186     if (fd >= 0)
2187         close(fd);
2188
2189     return ret;
2190 }
2191 #endif /* SUSE */
2192
2193 static struct option long_options[] =
2194 {
2195     {"verbose", 0, (int*)0, 'v'},
2196     {"config",  1, (int*)0, 'c'},
2197     {"dryrun",  0, (int*)0, 'n'},
2198     {"default", 0, (int*)0, 'd'},
2199     {"remove",  0, (int*)0, 'r'},
2200     {"force",   0, (int*)0, 'f'},
2201     {"path",    1, (int*)0, 'p'},
2202     {"override",1, (int*)0, 'o'},
2203     {"help",    0, (int*)0, 'h'},
2204     { 0,        0, (int*)0,  0 },
2205 };
2206
2207 static void help(const char *restrict const name) attribute((nonnull(1)));
2208 static void help(const char *restrict const  name)
2209 {
2210     printf("Usage: %s [<options>] [init_script|init_directory]\n", name);
2211     printf("Available options:\n");
2212     printf("  -h, --help       This help.\n");
2213     printf("  -r, --remove     Remove the listed scripts from all runlevels.\n");
2214     printf("  -f, --force      Ignore if a required service is missed.\n");
2215     printf("  -v, --verbose    Provide information on what is being done.\n");
2216     printf("  -p <path>, --path <path>  Path to replace " INITDIR ".\n");
2217     printf("  -o <path>, --override <path> Path to replace " OVERRIDEDIR ".\n");
2218     printf("  -c <config>, --config <config>  Path to config file.\n");
2219     printf("  -n, --dryrun     Do not change the system, only talk about it.\n");
2220     printf("  -d, --default    Use default runlevels a defined in the scripts\n");
2221 }
2222
2223
2224 /*
2225  * Do the job.
2226  */
2227 int main (int argc, char *argv[])
2228 {
2229     DIR * initdir;
2230     struct dirent *d;
2231     struct stat st_script;
2232     extension char * argr[argc];
2233     char * path = INITDIR;
2234     char * override_path = OVERRIDEDIR;
2235     char * insconf = INSCONF;
2236     const char *const ipath = path;
2237     int runlevel, c, dfd;
2238     boolean del = false;
2239     boolean defaults = false;
2240     boolean ignore = false;
2241
2242     myname = basename(*argv);
2243
2244 #ifdef SUSE
2245     if (underrpm())
2246         ignore = true;
2247 #endif /* SUSE */
2248
2249     if (getuid() == (uid_t)0)
2250         o_flags |= O_NOATIME;
2251
2252     for (c = 0; c < argc; c++)
2253         argr[c] = (char*)0;
2254
2255     while ((c = getopt_long(argc, argv, "c:dfrhvno:p:", long_options, (int *)0)) != -1) {
2256         size_t l;
2257         switch (c) {
2258             case 'c':
2259                 if (optarg == (char*)0 || *optarg == '\0')
2260                     goto err;
2261                 insconf = optarg;
2262                 set_insconf = true;
2263                 break;
2264             case 'd':
2265                 defaults = true;
2266                 break;
2267             case 'r':
2268                 del = true;
2269                 break;
2270             case 'f':
2271                 ignore = true;
2272                 break;
2273             case 'v':
2274                 verbose = true;
2275                 break;
2276             case 'n':
2277                 verbose = true;
2278                 dryrun = true;
2279                 break;
2280             case 'p':
2281                 if (optarg == (char*)0 || *optarg == '\0')
2282                     goto err;
2283                 if (path != ipath) free(path);
2284                 l = strlen(optarg) - 1;
2285                 path = xstrdup(optarg);
2286                 if (*(path+l) == '/')
2287                     *(path+l) = '\0';
2288                 break;
2289             case 'o':
2290                 if (optarg == (char*)0 || *optarg == '\0')
2291                     goto err;
2292                 override_path = optarg;
2293                 set_override = true;
2294                 break;
2295             case '?':
2296             err:
2297                 error("For help use: %s -h\n", myname);
2298             case 'h':
2299                 help(myname);
2300                 exit(0);
2301             default:
2302                 break;
2303         }
2304     }
2305     argv += optind;
2306     argc -= optind;
2307
2308     if (!argc && del)
2309         error("usage: %s [[-r] init_script|init_directory]\n", myname);
2310
2311     if (*argv) {
2312         char * token = strpbrk(*argv, delimeter);
2313
2314         /*
2315          * Let us separate the script/service name from the additional arguments.
2316          */
2317         if (token && *token) {
2318             *token = '\0';
2319             *argr = ++token;
2320         }
2321
2322         /* Catch `/path/script', `./script', and `path/script' */
2323         if (strchr(*argv, '/')) {
2324             if (stat(*argv, &st_script) < 0)
2325                 error("%s: %s\n", *argv, strerror(errno));
2326         } else {
2327             pushd(path);
2328             if (stat(*argv, &st_script) < 0)
2329                 error("%s: %s\n", *argv, strerror(errno));
2330             popd();
2331         }
2332
2333         if (S_ISDIR(st_script.st_mode)) {
2334             const size_t l = strlen(*argv) - 1;
2335
2336             if (path != ipath) free(path);
2337             path = xstrdup(*argv);
2338             if (*(path+l) == '/')
2339                 *(path+l) = '\0';
2340
2341             argv++;
2342             argc--;
2343             if (argc || del)
2344                 error("usage: %s [[-r] init_script|init_directory]\n", myname);
2345
2346         } else {
2347             char * base, * ptr = xstrdup(*argv);
2348
2349             if ((base = strrchr(ptr, '/'))) {
2350                 if (path != ipath) free(path);
2351                 *base = '\0';
2352                 path  = ptr;
2353             } else
2354                 free(ptr);
2355         }
2356     }
2357
2358     if (strcmp(path, INITDIR) != 0) {
2359         char * tmp;
2360         root = xstrdup(path);
2361         if ((tmp = strstr(root, INITDIR))) {
2362             *tmp = '\0';
2363         } else {
2364             free(root);
2365             root = (char*)0;
2366         }
2367     }
2368
2369     c = argc;
2370     while (c--) {
2371         char * base;
2372         char * token = strpbrk(argv[c], delimeter);
2373
2374         /*
2375          * Let us separate the script/service name from the additional arguments.
2376          */
2377         if (token && *token) {
2378             *token = '\0';
2379             argr[c] = ++token;
2380         }
2381
2382         if (stat(argv[c], &st_script) < 0) {
2383             if (errno != ENOENT)
2384                 error("%s: %s\n", argv[c], strerror(errno));
2385             pushd(path);
2386             if (stat(argv[c], &st_script) < 0)
2387                 error("%s: %s\n", argv[c], strerror(errno));
2388             popd();
2389         }
2390         if ((base = strrchr(argv[c], '/'))) {
2391             base++;
2392             argv[c] = base;
2393         }
2394     }
2395
2396 #if defined(DEBUG) && (DEBUG > 0)
2397     for (c = 0; c < argc; c++)
2398         if (argr[c])
2399             printf("Overwrite argument for %s is %s\n", argv[c], argr[c]);
2400 #endif /* DEBUG */
2401
2402     /*
2403      * Scan and set our configuration for virtual services.
2404      */
2405     scan_conf(insconf);
2406
2407     /*
2408      * Expand system facilities to real serivces
2409      */
2410     expand_conf();
2411
2412     /*
2413      * Initialize the regular scanner for the scripts.
2414      */
2415     scan_script_regalloc();
2416
2417     /*
2418      * Scan always for the runlevel links to see the current
2419      * link scheme of the services.
2420      */
2421     scan_script_locations(path, override_path, ignore);
2422
2423     /*
2424      * Clear out aliases found for scripts found up to this point.
2425      */
2426     clear_all();
2427
2428     /*
2429      * Open the script directory
2430      */
2431     if ((initdir = opendir(path)) == (DIR*)0 || (dfd = dirfd(initdir)) < 0)
2432         error("can not opendir(%s): %s\n", path, strerror(errno));
2433
2434 #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
2435     (void)posix_fadvise(dfd, 0, 0, POSIX_FADV_WILLNEED);
2436     (void)posix_fadvise(dfd, 0, 0, POSIX_FADV_SEQUENTIAL);
2437 #endif
2438
2439     /*
2440      * Now scan for the service scripts and their LSB comments.
2441      */
2442     pushd(path);
2443
2444     /*
2445      * Scan scripts found in the command line to be able to resolve
2446      * all dependcies given within those scripts.
2447      */
2448     if (argc > 1) for (c = 0; c < argc; c++) {
2449         const char *const name = argv[c];
2450         service_t * first = (service_t*)0;
2451         char * provides, * begin, * token;
2452         const uchar lsb = scan_script_defaults(dfd, name, override_path, (char**)0, true, ignore);
2453
2454         if ((lsb & FOUND_LSB_HEADER) == 0) {
2455             if ((lsb & (FOUND_LSB_DEFAULT|FOUND_LSB_OVERRIDE)) == 0)
2456                 warn("warning: script '%s' missing LSB tags and overrides\n", name);
2457             else
2458                 warn("warning: script '%s' missing LSB tags\n", name);
2459         }
2460
2461         if (!script_inf.provides || script_inf.provides == empty)
2462             script_inf.provides = xstrdup(name);
2463
2464         provides = xstrdup(script_inf.provides);
2465         begin = provides;
2466         while ((token = strsep(&begin, delimeter)) && *token) {
2467             service_t * service;
2468
2469             if (*token == '$') {
2470                 warn("script %s provides system facility %s, skipped!\n", name, token);
2471                 continue;
2472             }
2473             if (*token == '#') {
2474                 warn("script %s provides facility %s with comment sign, skipped!\n", name, token);
2475                 continue;
2476             }
2477
2478             service = addservice(token);
2479
2480             if (first)
2481                 nickservice(first, service);
2482             else
2483                 first = service;
2484
2485             service->attr.flags |= SERV_CMDLINE;
2486         }
2487         free(provides);
2488     }
2489
2490     /*
2491      * Scan now all scripts found in the init.d/ directory
2492      */
2493     while ((d = readdir(initdir)) != (struct dirent*)0) {
2494         const boolean isarg = chkfor(d->d_name, argv, argc);
2495         service_t * service = (service_t*)0;
2496         char * token;
2497         char * begin = (char*)0;        /* hold start pointer of strings handled by strsep() */
2498         boolean hard = false;
2499         uchar lsb = 0;
2500 #if defined(DEBUG) && (DEBUG > 0)
2501         int nobug = 0;
2502 #endif
2503
2504         if (*d->d_name == '.')
2505             continue;
2506         errno = 0;
2507
2508         /* d_type seems not to work, therefore use (l)stat(2) */
2509         if (xstat(dfd, d->d_name, &st_script) < 0) {
2510             warn("can not stat(%s)\n", d->d_name);
2511             continue;
2512         }
2513         if (!S_ISREG(st_script.st_mode) || !(S_IXUSR & st_script.st_mode))
2514         {
2515             if (S_ISDIR(st_script.st_mode))
2516                 continue;
2517             if (isarg)
2518                 warn("script %s is not an executable regular file, skipped!\n", d->d_name);
2519             continue;
2520         }
2521
2522         if (!strncmp(d->d_name, "README", strlen("README"))) {
2523             if (isarg)
2524                 warn("script name %s is not valid, skipped!\n", d->d_name);
2525             continue;
2526         }
2527
2528         if (!strncmp(d->d_name, "Makefile", strlen("Makefile"))) {
2529             if (isarg)
2530                 warn("script name %s is not valid, skipped!\n", d->d_name);
2531             continue;
2532         }
2533
2534         if (!strncmp(d->d_name, "core", strlen("core"))) {
2535             if (isarg)
2536                 warn("script name %s is not valid, skipped!\n", d->d_name);
2537             continue;
2538         }
2539
2540         /* Common scripts not used within runlevels */
2541         if (!strcmp(d->d_name, "rx")       ||
2542             !strncmp(d->d_name, "skeleton", 8) ||
2543             !strncmp(d->d_name, "powerfail", 9))
2544         {
2545             if (isarg)
2546                 warn("script name %s is not valid, skipped!\n", d->d_name);
2547             continue;
2548         }
2549
2550 #ifdef SUSE
2551         if (!strcmp(d->d_name, "boot") || !strcmp(d->d_name, "rc"))
2552 #else  /* not SUSE */
2553         if (!strcmp(d->d_name, "rcS") || !strcmp(d->d_name, "rc"))
2554 #endif /* not SUSE */
2555         {
2556             if (isarg)
2557                 warn("script name %s is not valid, skipped!\n", d->d_name);
2558             continue;
2559         }
2560
2561         if (cfgfile_filter(d) == 0) {
2562             if (isarg)
2563                 warn("script name %s is not valid, skipped!\n", d->d_name);
2564             continue;
2565         }
2566
2567         /* left by emacs like editors */
2568         if (d->d_name[strlen(d->d_name)-1] == '~') {
2569             if (isarg)
2570                 warn("script name %s is not valid, skipped!\n", d->d_name);
2571             continue;
2572         }
2573
2574         if (strspn(d->d_name, "$.#%_+-\\*[]^:()~")) {
2575             if (isarg)
2576                 warn("script name %s is not valid, skipped!\n", d->d_name);
2577             continue;
2578         }
2579
2580         /* main scanner for LSB comment in current script */
2581         lsb = scan_script_defaults(dfd, d->d_name, override_path, (char**)0, false, ignore);
2582
2583         if ((lsb & FOUND_LSB_HEADER) == 0) {
2584             if ((lsb & (FOUND_LSB_DEFAULT|FOUND_LSB_OVERRIDE)) == 0)
2585                 warn("warning: script '%s' missing LSB tags and overrides\n", d->d_name);
2586             else
2587                 warn("warning: script '%s' missing LSB tags\n", d->d_name);
2588         }
2589
2590 #ifdef SUSE
2591         /* Common script ... */
2592         if (!strcmp(d->d_name, "halt")) {
2593             service_t *serv = addservice("halt");
2594             serv = getorig(serv);
2595             makeprov(serv,   d->d_name);
2596             runlevels(serv, 'S', "0");
2597             serv->attr.flags |= (SERV_ALL|SERV_NOSTOP|SERV_INTRACT);
2598             continue;
2599         }
2600
2601         /* ... and its link */
2602         if (!strcmp(d->d_name, "reboot")) {
2603             service_t *serv = addservice("reboot");
2604             serv = getorig(serv);
2605             makeprov(serv,   d->d_name);
2606             runlevels(serv, 'S', "6");
2607             serv->attr.flags |= (SERV_ALL|SERV_NOSTOP|SERV_INTRACT);
2608             continue;
2609         }
2610
2611         /* Common script for single mode */
2612         if (!strcmp(d->d_name, "single")) {
2613             service_t *serv = addservice("single");
2614             serv = getorig(serv);
2615             makeprov(serv,   d->d_name);
2616             runlevels(serv, 'S', "1 S");
2617             serv->attr.flags |= (SERV_ALL|SERV_NOSTOP|SERV_INTRACT);
2618             rememberreq(serv, REQ_SHLD, "kbd");
2619             continue;
2620         }
2621 #endif /* SUSE */
2622
2623 #ifndef SUSE
2624         if (!lsb) {
2625             script_inf.required_start = xstrdup(DEFAULT_DEPENDENCY);
2626             script_inf.required_stop = xstrdup(DEFAULT_DEPENDENCY);
2627             script_inf.default_start = xstrdup(DEFAULT_START_LVL);
2628             script_inf.default_stop = xstrdup(DEFAULT_STOP_LVL);
2629         }
2630 #endif /* not SUSE */
2631
2632         /*
2633          * Oops, no comment found, guess one
2634          */
2635         if (!script_inf.provides || script_inf.provides == empty) {
2636             service_t * guess;
2637             script_inf.provides = xstrdup(d->d_name);
2638
2639             /*
2640              * Use guessed service to find it within the the runlevels
2641              * (by using the list from the first scan for script locations).
2642              */
2643             if ((guess = findservice(script_inf.provides))) {
2644                 /*
2645                  * Try to guess required services out from current scheme.
2646                  * Note, this means that all services are required.
2647                  */
2648                 if (!script_inf.required_start || script_inf.required_start == empty) {
2649                     list_t * ptr;
2650                     list_for_each_prev(ptr, s_start) {
2651                         service_t * tmp = getservice(ptr);
2652                         tmp = getorig(tmp);
2653                         if (!tmp->attr.sorder)
2654                             continue;
2655                         if (tmp->attr.sorder >= guess->attr.sorder)
2656                             continue;
2657                         if (tmp->start->lvl & guess->start->lvl) {
2658                             script_inf.required_start = xstrdup(tmp->name);
2659                             break;
2660                         }
2661                     }
2662                 }
2663                 if (!script_inf.required_stop || script_inf.required_stop == empty) {
2664                     list_t * ptr;
2665                     list_for_each_prev(ptr, s_start) {
2666                         service_t * tmp = getservice(ptr);
2667                         tmp = getorig(tmp);
2668                         if (!tmp->attr.korder)
2669                             continue;
2670                         if (tmp->attr.korder <= guess->attr.korder)
2671                             continue;
2672                         if (tmp->stopp->lvl & guess->stopp->lvl) {
2673                             script_inf.required_stop = xstrdup(tmp->name);
2674                             break;
2675                         }
2676                     }
2677                 }
2678                 if (!script_inf.default_start || script_inf.default_start == empty) {
2679                     if (guess->start->lvl)
2680                         script_inf.default_start = lvl2str(guess->start->lvl);
2681                 }
2682                 if (!script_inf.default_stop || script_inf.default_stop == empty) {
2683                     if (guess->stopp->lvl)
2684                         script_inf.default_stop = lvl2str(guess->stopp->lvl);
2685                 }
2686
2687             } else {    /* !findservice(&guess, script_inf.provides) */
2688
2689                 list_t * ptr;
2690                 /*
2691                  * Find out which levels this service may have out from current scheme.
2692                  * Note, this means that the first requiring service wins.
2693                  */
2694                 list_for_each(ptr, s_start) {
2695                     service_t * cur;
2696                     list_t * req;
2697
2698                     if (script_inf.default_start && script_inf.default_start != empty)
2699                            break;
2700                     cur = getservice(ptr);
2701                     cur = getorig(cur);
2702
2703                     if (list_empty(&cur->sort.req) || !(cur->attr.flags & SERV_ENABLED))
2704                         continue;
2705
2706                     np_list_for_each(req, &cur->sort.req) {
2707                         if (!strcmp(getreq(req)->serv->name, script_inf.provides)) {
2708                             script_inf.default_start = lvl2str(getservice(ptr)->start->lvl);
2709                             break;
2710                         }
2711                     }
2712                 }
2713                 list_for_each(ptr, s_start) {
2714                     service_t * cur;
2715                     list_t * rev;
2716
2717                     if (script_inf.default_stop && script_inf.default_stop != empty)
2718                            break;
2719                     cur = getservice(ptr);
2720                     cur = getorig(cur);
2721
2722                     if (list_empty(&cur->sort.rev) || !(cur->attr.flags & SERV_ENABLED))
2723                         continue;
2724
2725                     np_list_for_each(rev, &cur->sort.rev) {
2726                         if (!strcmp(getreq(rev)->serv->name, script_inf.provides)) {
2727                             script_inf.default_stop = lvl2str(getservice(ptr)->stopp->lvl);
2728                             break;
2729                         }
2730                     }
2731                 }
2732             }           /* !findservice(&guess, script_inf.provides) */
2733         }
2734
2735         /*
2736          * Use guessed service to find it within the the runlevels
2737          * (by using the list from the first scan for script locations).
2738          */
2739         if (!service) {
2740             char * provides = xstrdup(script_inf.provides);
2741             service_t * first = (service_t*)0;
2742
2743             begin = provides;
2744             while ((token = strsep(&begin, delimeter)) && *token) {
2745
2746                 if (*token == '$') {
2747                     warn("script %s provides system facility %s, skipped!\n", d->d_name, token);
2748                     continue;
2749                 }
2750                 if (*token == '#') {
2751                     warn("script %s provides facility %s with comment sign, skipped!\n", d->d_name, token);
2752                     continue;
2753                 }
2754
2755                 service = addservice(token);
2756  
2757                 if (first)
2758                     nickservice(first, service);
2759                 else
2760                     first = service;
2761
2762 #if defined(DEBUG) && (DEBUG > 0)
2763                 nobug++;
2764 #endif
2765                 if (!makeprov(service, d->d_name)) {
2766
2767                     if (!del || (del && !isarg))
2768                         warn("script %s: service %s already provided!\n", d->d_name, token);
2769
2770                     if (!del && !ignore && isarg)
2771                         error("exiting now!\n");
2772
2773                     if (!del || (del && !ignore && !isarg))
2774                         continue;
2775
2776                     /* Provide this service with an other name to be able to delete it */
2777                     service = addservice(d->d_name);
2778                     service = getorig(service);
2779                     service->attr.flags |= SERV_ALREADY;
2780                     (void)makeprov(service, d->d_name);
2781
2782                     continue;
2783                 }
2784
2785                 if (service) {
2786                     boolean known = (service->attr.flags & SERV_KNOWN);
2787                     service->attr.flags |= SERV_KNOWN;
2788
2789                     if (!known) {
2790                         if (script_inf.required_start && script_inf.required_start != empty) {
2791                             rememberreq(service, REQ_MUST, script_inf.required_start);
2792 #ifdef USE_COMPAT_EMPTY
2793                             if (!script_inf.required_stop || script_inf.required_stop == empty)
2794                                 script_inf.required_stop = xstrdup(script_inf.required_start);
2795 #endif /* USE_COMPAT_EMPTY */
2796                         }
2797                         if (script_inf.should_start && script_inf.should_start != empty) {
2798                             rememberreq(service, REQ_SHLD, script_inf.should_start);
2799 #ifdef USE_COMPAT_EMPTY
2800                             if (!script_inf.should_stop || script_inf.should_stop == empty)
2801                                 script_inf.should_stop = xstrdup(script_inf.should_start);
2802 #endif /* USE_COMPAT_EMPTY */
2803                         }
2804                         if (script_inf.required_stop && script_inf.required_stop != empty) {
2805                             rememberreq(service, REQ_MUST|REQ_KILL, script_inf.required_stop);
2806                         }
2807                         if (script_inf.should_stop && script_inf.should_stop != empty) {
2808                             rememberreq(service, REQ_SHLD|REQ_KILL, script_inf.should_stop);
2809                         }
2810                     }
2811
2812                     if (script_inf.start_before && script_inf.start_before != empty) {
2813                         reversereq(service, REQ_SHLD, script_inf.start_before);
2814 #ifdef USE_COMPAT_EMPTY
2815                         if (!script_inf.stop_after || script_inf.stop_after == empty)
2816                             script_inf.stop_after = xstrdup(script_inf.start_before);
2817 #endif /* USE_COMPAT_EMPTY */
2818                     }
2819                     if (script_inf.stop_after && script_inf.stop_after != empty) {
2820                         reversereq(service, REQ_SHLD|REQ_KILL, script_inf.stop_after);
2821                     }
2822                     /*
2823                      * Use information from symbolic link structure to
2824                      * check if all services are around for this script.
2825                      */
2826                     if (isarg && !ignore) {
2827                         boolean ok = true;
2828                         if (del)
2829                             ok = chkdependencies(service);
2830                         else
2831                             ok = chkrequired(service);
2832                         if (!ok && !ignore)
2833                             error("exiting now!\n");
2834                     }
2835
2836                     if (script_inf.default_start && script_inf.default_start != empty) {
2837                         ushort deflvls = str2lvl(script_inf.default_start);
2838
2839                         if (service->attr.flags & SERV_ENABLED) {
2840                             /*
2841                              * Currently linked into service runlevel scheme, check
2842                              * if the defaults are overwriten. Compare all bits,
2843                              * which means `==' and not `&' and overwrite the defaults
2844                              * of the current script.
2845                              */
2846                             if (!defaults && (deflvls != service->start->lvl)) {
2847                                 if (!del && isarg && !(argr[curr_argc]))
2848                                     warn("warning: current start runlevel(s) (%s) of script `%s' overwrites defaults (%s).\n",
2849                                          service->start->lvl ? lvl2str(service->start->lvl) : "empty", d->d_name, lvl2str(deflvls));
2850                             }
2851                         } else
2852                             /*
2853                              * Currently not linked into service runlevel scheme, info
2854                              * needed for enabling interactive services at first time.
2855                              */
2856                             service->start->lvl = deflvls;
2857
2858                     } else if (script_inf.default_start == empty) {
2859                         if (service->attr.flags & SERV_ENABLED) {
2860                             /*
2861                              * Currently linked into service runlevel scheme, check
2862                              * if the defaults are overwriten. Compare all bits,
2863                              * which means `==' and not `&' and overwrite the defaults
2864                              * of the current script.
2865                              */
2866                             if (!defaults && service->start->lvl != 0) {
2867                                 warn("warning: current start runlevel(s) (%s) of script `%s' overwrites defaults (empty).\n",
2868                                      lvl2str(service->start->lvl), d->d_name);
2869                                 script_inf.default_start = lvl2str(service->start->lvl);
2870                             }
2871                         }
2872                     } else if (!script_inf.default_start && (service->attr.flags & SERV_NOTLSB)) {
2873 #ifdef SUSE
2874                         /*
2875                          * Could be a none LSB script, use info from current link scheme.
2876                          * If not found use default.
2877                          */
2878                         if (service->attr.flags & SERV_ENABLED)
2879                             script_inf.default_start = lvl2str(service->start->lvl);
2880                         else
2881                             script_inf.default_start = xstrdup(DEFAULT_START_LVL);
2882 #endif /* SUSE */
2883                     }
2884 #ifdef SUSE
2885                     /*
2886                      * This because SuSE boot script concept uses a differential link scheme.
2887                      * Therefore default_stop is ignored and overwriten by default_start.
2888                      */
2889                     xreset(script_inf.default_stop);
2890                     if (script_inf.default_start && script_inf.default_start != empty)
2891                         script_inf.default_stop = xstrdup(script_inf.default_start);
2892                     else
2893                         script_inf.default_stop = empty;
2894                     oneway(script_inf.default_stop);
2895 #endif /* SUSE */
2896                     if (script_inf.default_stop && script_inf.default_stop != empty) {
2897                         ushort deflvlk = str2lvl(script_inf.default_stop);
2898
2899                         /*
2900                          * Compare all bits, which means `==' and not `&' and overwrite
2901                          * the defaults of the current script.
2902                          */
2903                         if (service->attr.flags & SERV_ENABLED) {
2904                             /*
2905                              * Currently linked into service runlevel scheme, check
2906                              * if the defaults are overwriten.
2907                              */
2908                             if (!defaults && (deflvlk != service->stopp->lvl)) {
2909                                 if (!del && isarg && !(argr[curr_argc]))
2910                                     warn("warning: current stop runlevel(s) (%s) of script `%s' overwrites defaults (%s).\n",
2911                                          service->stopp->lvl ? lvl2str(service->stopp->lvl) : "empty", d->d_name, lvl2str(deflvlk));
2912                             }
2913                         } else
2914                             /*
2915                              * Currently not linked into service runlevel scheme, info
2916                              * needed for enabling interactive services at first time.
2917                              */
2918                             service->stopp->lvl = deflvlk;
2919
2920                     } else if (script_inf.default_stop == empty) {
2921                         if (service->attr.flags & SERV_ENABLED) {
2922                             /*
2923                              * Currently linked into service runlevel scheme, check
2924                              * if the defaults are overwriten. Compare all bits,
2925                              * which means `==' and not `&' and overwrite the defaults
2926                              * of the current script.
2927                              */
2928                             if (!defaults && service->stopp->lvl != 0) {
2929                                 warn("warning: current stop runlevel(s) (%s) of script `%s' overwrites defaults (empty).\n",
2930                                      lvl2str(service->stopp->lvl), d->d_name);
2931                                 script_inf.default_stop = lvl2str(service->stopp->lvl);
2932                             }
2933                         }
2934                     } else if (!script_inf.default_stop && (service->attr.flags & SERV_NOTLSB)) {
2935 #ifdef SUSE
2936                         /*
2937                          * Could be a none LSB script, use info from current link scheme.
2938                          * If not found use default.
2939                          */
2940                         if (service->attr.flags & SERV_ENABLED)
2941                             script_inf.default_stop = lvl2str(service->stopp->lvl);
2942                         else
2943                             script_inf.default_stop = xstrdup(DEFAULT_STOP_LVL);
2944 #endif /* SUSE */
2945                     }
2946                 }
2947             }
2948             free(provides);
2949         }
2950
2951 #ifdef SUSE
2952         /* Ahh ... set default multiuser with network */
2953         if (!script_inf.default_start || script_inf.default_start == empty) {
2954             if (!script_inf.default_start)
2955                 warn("Default-Start undefined, assuming default start runlevel(s) for script `%s'\n", d->d_name);
2956             script_inf.default_start = xstrdup(DEFAULT_START_LVL);
2957             xreset(script_inf.default_stop);
2958             script_inf.default_stop = xstrdup(script_inf.default_start);
2959             oneway(script_inf.default_stop);
2960         }
2961 #else  /* not SUSE */
2962         if (!script_inf.default_start) {
2963             warn("Default-Start undefined, assuming empty start runlevel(s) for script `%s'\n", d->d_name);
2964             script_inf.default_start = empty;
2965         }
2966 #endif /* not SUSE */
2967
2968 #ifdef SUSE
2969         if (!script_inf.default_stop || script_inf.default_stop == empty) {
2970             if (script_inf.default_start && script_inf.default_start != empty)
2971                 script_inf.default_stop = xstrdup(script_inf.default_start);
2972             else
2973                 script_inf.default_stop = xstrdup(DEFAULT_STOP_LVL);
2974             oneway(script_inf.default_stop);
2975         }
2976 #else  /* not SUSE */
2977         if (!script_inf.default_stop) {
2978             warn("Default-Stop  undefined, assuming empty stop  runlevel(s) for script `%s'\n", d->d_name);
2979             script_inf.default_stop = empty;
2980         }
2981 #endif /* not SUSE */
2982
2983         if (isarg && !defaults && !del) {
2984             if (argr[curr_argc]) {
2985                 char * ptr = argr[curr_argc];
2986                 struct _mark {
2987                     const char * wrd;
2988                     char * order;
2989                     char ** str;
2990                 } mark[] = {
2991                     {"start=",    (char*)0, &script_inf.default_start},
2992                     {"stop=",     (char*)0, &script_inf.default_stop },
2993 #if 0
2994                     {"reqstart=", (char*)0, &script_inf.required_start},
2995                     {"reqstop=",  (char*)0, &script_inf.required_stop },
2996 #endif
2997                     {(char*)0,    (char*)0, (char**)0}
2998                 };
2999
3000                 for (c = 0; mark[c].wrd; c++) {
3001                     char * order = strstr(ptr, mark[c].wrd);
3002                     if (order)
3003                         mark[c].order = order;
3004                 }
3005
3006                 for (c = 0; mark[c].wrd; c++)
3007                     if (mark[c].order) {
3008                         *(mark[c].order) = '\0';
3009                         mark[c].order += strlen(mark[c].wrd);
3010                     }
3011
3012                 for (c = 0; mark[c].wrd; c++)
3013                     if (mark[c].order) {
3014                         size_t len = strlen(mark[c].order);
3015                         if (len > 0) {
3016                             char * ptr = mark[c].order + len - 1;
3017                             if (*ptr == ',') *ptr = '\0';
3018                         }
3019                         xreset(*(mark[c].str));
3020                         *(mark[c].str) = xstrdup(mark[c].order);
3021                     }
3022                 hard = true;
3023 #ifdef SUSE
3024                 /*
3025                  * This because SuSE boot script concept uses a differential link scheme.
3026                  * Therefore default_stop is ignored and overwriten by default_start.
3027                  */
3028                 if (strcmp(script_inf.default_stop, script_inf.default_start) != 0) {
3029                     xreset(script_inf.default_stop);
3030                     script_inf.default_stop = xstrdup(script_inf.default_start);
3031                     oneway(script_inf.default_stop);
3032                 }
3033 #endif /* SUSE */
3034             }
3035         }
3036
3037 #if defined(DEBUG) && (DEBUG > 0)
3038         if (!nobug) {
3039             fprintf(stderr, "internal BUG at line %d with script %s\n", __LINE__, d->d_name);
3040             exit(1);
3041         }
3042 #endif
3043
3044         begin = script_inf.provides;
3045         while ((token = strsep(&script_inf.provides, delimeter)) && *token) {
3046             if (*token == '$')
3047                 continue;
3048             if (*token == '#')
3049                 continue;
3050             if (!service)
3051                 service = addservice(token);
3052             service = getorig(service);
3053
3054             if ((service->attr.flags & SERV_ENABLED) && !hard) {
3055                 if (del)
3056                     continue;
3057                 if (!defaults)
3058                     continue;
3059             }
3060
3061             if (script_inf.default_start && script_inf.default_start != empty)
3062                 runlevels(service, 'S', script_inf.default_start);
3063             if (script_inf.default_stop && script_inf.default_stop != empty)
3064                 runlevels(service, 'K', script_inf.default_stop);
3065         }
3066         script_inf.provides = begin;
3067
3068         /* Remember if not LSB conform script */
3069         if (!lsb && service) {
3070             service = getorig(service);
3071             service->attr.flags |= SERV_NOTLSB;
3072         }
3073     }
3074     /* Reset remaining pointers */
3075     scan_script_reset();
3076
3077     /*
3078      * Free the regular scanner for the scripts.
3079      */
3080     scan_script_regfree();
3081
3082     /* back */
3083     popd();
3084     closedir(initdir);
3085
3086     /*
3087      * Clear out aliases found for all scripts.
3088      */
3089     clear_all();
3090
3091     /*
3092      * Set virtual dependencies for already enabled none LSB scripts.
3093      */
3094     nonlsb_script();
3095
3096     /*
3097      * Now generate for all scripts the dependencies
3098      */
3099     follow_all();
3100     if (is_loop_detected() && !ignore)
3101         error("exiting without changing boot order!\n");
3102
3103     /*
3104      * Be sure that interactive scripts are the only member of
3105      * a start group (for parallel start only).
3106      */
3107     active_script();
3108
3109     /*
3110      * Move the `$all' scripts to the end of all
3111      */
3112     all_script();
3113
3114     /*
3115      * Sorry but we support only [KS][0-9][0-9]<name>
3116      */
3117     if (maxstart > MAX_DEEP || maxstop > MAX_DEEP)
3118         error("Maximum of %u in ordering reached\n", MAX_DEEP);
3119
3120 #if defined(DEBUG) && (DEBUG > 0)
3121     printf("Maxorder %d/%d\n", maxstart, maxstop);
3122     show_all();
3123 #else
3124 # ifdef SUSE    /* SuSE's SystemV link scheme */
3125     pushd(path);
3126     for (runlevel = 0; runlevel < RUNLEVLES; runlevel++) {
3127         const ushort lvl = map_runlevel_to_lvl(runlevel);
3128         char nlink[PATH_MAX+1], olink[PATH_MAX+1];
3129         const char * rcd = (char*)0;
3130         const char * script;
3131         service_t *serv;
3132         DIR  * rcdir;
3133
3134         if ((rcd = map_runlevel_to_location(runlevel)) == (char*)0)
3135             continue;
3136
3137         rcdir = openrcdir(rcd);         /* Creates runlevel directory if necessary */
3138         if (rcdir == (DIR*)0)
3139             break;
3140         if ((dfd = dirfd(rcdir)) < 0) {
3141             closedir(rcdir);
3142             break;
3143         }
3144         pushd(rcd);
3145
3146         /*
3147          * See if we found scripts which should not be
3148          * included within this runlevel directory.
3149          */
3150         while ((d = readdir(rcdir)) != (struct dirent*)0) {
3151             const char * ptr = d->d_name;
3152             char type;
3153
3154             if (*ptr != 'S' && *ptr != 'K')
3155                 continue;
3156             type = *ptr;
3157             ptr++;
3158
3159             if (strspn(ptr, "0123456789") != 2)
3160                 continue;
3161             ptr += 2;
3162
3163             if (xstat(dfd, d->d_name, &st_script) < 0)
3164                 xremove(dfd, d->d_name);        /* dangling sym link */
3165
3166             if (notincluded(ptr, type, runlevel)) {
3167                 serv = findservice(getprovides(ptr));
3168                 if (defaults) {
3169                     xremove(dfd, d->d_name);
3170                     if (serv && --serv->attr.ref <= 0)
3171                         serv->attr.flags &= ~SERV_ENABLED;
3172                 } else if (lvl & LVL_ONEWAY) {
3173                     xremove(dfd, d->d_name);
3174                     if (serv && --serv->attr.ref <= 0)
3175                         serv->attr.flags &= ~SERV_ENABLED;
3176                 } else if (del && ignore) {
3177                     if (serv && (serv->attr.flags & SERV_ALREADY)) {
3178                         xremove(dfd, d->d_name);
3179                         if (--serv->attr.ref <= 0)
3180                             serv->attr.flags &= ~SERV_ENABLED;
3181                     }
3182                 }
3183             }
3184         }
3185
3186         /*
3187          * Seek for scripts which are included, link or
3188          * correct order number if necessary.
3189          */
3190
3191         script = (char*)0;
3192         while ((serv = listscripts(&script, 'X', lvl))) {
3193             const boolean this = chkfor(script, argv, argc);
3194             boolean found, slink;
3195             char * clink;
3196
3197             if (*script == '$')         /* Do not link in virtual dependencies */
3198                 continue;
3199
3200             slink = false;
3201             if ((serv->start->lvl & lvl) == 0)
3202                 goto stop;
3203
3204             sprintf(olink, "../%s",   script);
3205             sprintf(nlink, "S%.2d%s", serv->attr.sorder, script);
3206
3207             found = false;
3208             rewinddir(rcdir);
3209             while ((clink = scan_for(rcdir, script, 'S'))) {
3210                 found = true;
3211                 if (strcmp(clink, nlink)) {
3212                     xremove(dfd, clink);                /* Wrong order, remove link */
3213                     if (--serv->attr.ref <= 0)
3214                         serv->attr.flags &= ~SERV_ENABLED;
3215                     if (!this) {
3216                         xsymlink(dfd, olink, nlink);    /* Not ours, but correct order */
3217                         if (++serv->attr.ref)
3218                             serv->attr.flags |= SERV_ENABLED;
3219                     }
3220                     if (this && !del) {
3221                         xsymlink(dfd, olink, nlink);    /* Restore, with correct order */
3222                         if (++serv->attr.ref)
3223                             serv->attr.flags |= SERV_ENABLED;
3224                     }
3225                 } else {
3226                     if (del && this) {
3227                         xremove(dfd, clink);            /* Found it, remove link */
3228                         if (--serv->attr.ref <= 0)
3229                             serv->attr.flags &= ~SERV_ENABLED;
3230                     }
3231                 }
3232             }
3233
3234             if (this) {
3235                 /*
3236                  * If we haven't found it and we shouldn't delete it
3237                  * we try to add it.
3238                  */
3239                 if (!del && !found) {
3240                     xsymlink(dfd, olink, nlink);
3241                     if (++serv->attr.ref)
3242                         serv->attr.flags |= SERV_ENABLED;
3243                     found = true;
3244                 }
3245             }
3246
3247             /* Start links done, now do Kill links */
3248
3249             slink = found;
3250         stop:
3251             if ((serv->stopp->lvl & lvl) == 0)
3252                 continue;
3253
3254             sprintf(olink, "../%s",   script);
3255             sprintf(nlink, "K%.2d%s", serv->attr.korder, script);
3256
3257             found = false;
3258             rewinddir(rcdir);
3259             while ((clink = scan_for(rcdir, script, 'K'))) {
3260                 found = true;
3261                 if (strcmp(clink, nlink)) {
3262                     xremove(dfd, clink);                /* Wrong order, remove link */
3263                     if (--serv->attr.ref <= 0)
3264                         serv->attr.flags &= ~SERV_ENABLED;
3265                     if (!this) {
3266                         xsymlink(dfd, olink, nlink);    /* Not ours, but correct order */
3267                         if (++serv->attr.ref)
3268                             serv->attr.flags |= SERV_ENABLED;
3269                     }
3270                     if (this && !del) {
3271                         xsymlink(dfd, olink, nlink);    /* Restore, with correct order */
3272                         if (++serv->attr.ref)
3273                             serv->attr.flags |= SERV_ENABLED;
3274                     }
3275                 } else {
3276                     if (del && this) {
3277                         xremove(dfd, clink);            /* Found it, remove link */
3278                         if (--serv->attr.ref <= 0)
3279                             serv->attr.flags &= ~SERV_ENABLED;
3280                     }
3281                 }
3282             }
3283
3284             if (this && slink) {
3285                 /*
3286                  * If we haven't found it and we shouldn't delete it
3287                  * we try to add it.
3288                  */
3289                 if (!del && !found) {
3290                     xsymlink(dfd, olink, nlink);
3291                     if (++serv->attr.ref)
3292                         serv->attr.flags |= SERV_ENABLED;
3293                 }
3294             }
3295         }
3296         popd();
3297         closedir(rcdir);
3298     }
3299 # else  /* not SUSE but Debian SystemV link scheme */
3300    /*
3301     * Remark: At SuSE we use boot scripts for system initialization which
3302     * will be executed by /etc/init.d/boot (which is equal to rc.sysinit).
3303     * At system reboot or system halt the stop links of those boot scripts
3304     * will be executed by /etc/init.d/halt.  Don't know how todo this for
3305     * a traditional standard SystemV link scheme.  Maybe for such an
3306     * approach a new directory halt.d/ whould be an idea.
3307     */
3308     pushd(path);
3309     for (runlevel = 0; runlevel < RUNLEVLES; runlevel++) {
3310         char nlink[PATH_MAX+1], olink[PATH_MAX+1];
3311         const char * rcd = (char*)0;
3312         const char * script;
3313         service_t * serv;
3314         ushort lvl, seek;
3315         DIR  * rcdir;
3316
3317         if ((rcd = map_runlevel_to_location(runlevel)) == (char*)0)
3318             continue;
3319         lvl  = map_runlevel_to_lvl(runlevel);
3320         seek = map_runlevel_to_seek(runlevel);
3321
3322         rcdir = openrcdir(rcd);         /* Creates runlevel directory if necessary */
3323         if (rcdir == (DIR*)0)
3324             break;
3325         if ((dfd = dirfd(rcdir)) < 0) {
3326             closedir(rcdir);
3327             break;
3328         }
3329         pushd(rcd);
3330
3331         /*
3332          * See if we found scripts which should not be
3333          * included within this runlevel directory.
3334          */
3335         while ((d = readdir(rcdir)) != (struct dirent*)0) {
3336             const char * ptr = d->d_name;
3337             char type;
3338
3339             if (*ptr != 'S' && *ptr != 'K')
3340                 continue;
3341             type = *ptr;
3342             ptr++;
3343
3344             if (strspn(ptr, "0123456789") != 2)
3345                 continue;
3346             ptr += 2;
3347
3348             if (xstat(dfd, d->d_name, &st_script) < 0)
3349                 xremove(dfd, d->d_name);        /* dangling sym link */
3350
3351             if (notincluded(ptr, type, runlevel)) {
3352                 serv = findservice(getprovides(ptr));
3353                 if (defaults) {
3354                     xremove(dfd, d->d_name);
3355                     if (serv && --serv->attr.ref <= 0)
3356                         serv->attr.flags &= ~SERV_ENABLED;
3357 #  ifndef USE_KILL_IN_BOOT
3358                 } else if (lvl & LVL_BOOT) {
3359                     xremove(dfd, d->d_name);
3360                     if (serv && --serv->attr.ref <= 0)
3361                         serv->attr.flags &= ~SERV_ENABLED;
3362 #  endif /* USE_KILL_IN_BOOT */
3363                 } else if (del && ignore) {
3364                     if (serv && (serv->attr.flags & SERV_ALREADY))
3365                         xremove(dfd, d->d_name);
3366                         if (--serv->attr.ref <= 0)
3367                             serv->attr.flags &= ~SERV_ENABLED;
3368                 }
3369             }
3370         }
3371
3372         script = (char*)0;
3373         while ((serv = listscripts(&script, 'X', seek))) {
3374             const boolean this = chkfor(script, argv, argc);
3375             boolean found;
3376             char * clink;
3377             char mode;
3378
3379             if (*script == '$')         /* Do not link in virtual dependencies */
3380                 continue;
3381
3382             sprintf(olink, "../init.d/%s", script);
3383             if (serv->stopp->lvl & lvl) {
3384 #  ifndef USE_KILL_IN_BOOT
3385                 if (lvl & LVL_BOOT)                     /* No kill links in rcS.d */
3386                         continue;
3387 #  endif /* USE_KILL_IN_BOOT */
3388                 sprintf(nlink, "K%.2d%s", serv->attr.korder, script);
3389                 mode = 'K';
3390             } else if (serv->start->lvl & lvl) {
3391                 sprintf(nlink, "S%.2d%s", serv->attr.sorder, script);
3392                 mode = 'S';
3393             } else
3394                 continue;               /* We aren't suppose to be on this runlevel */
3395
3396             found = false;
3397
3398             rewinddir(rcdir);
3399             while ((clink = scan_for(rcdir, script, mode))) {
3400                 found = true;
3401                 if (strcmp(clink, nlink)) {
3402                     xremove(dfd, clink);                /* Wrong order, remove link */
3403                     if (--serv->attr.ref <= 0)
3404                         serv->attr.flags &= ~SERV_ENABLED;
3405                     if (!this) {
3406                         xsymlink(dfd, olink, nlink);    /* Not ours, but correct order */
3407                         if (++serv->attr.ref)
3408                             serv->attr.flags |= SERV_ENABLED;
3409                     }
3410                     if (this && !del) {
3411                         xsymlink(dfd, olink, nlink);    /* Restore, with correct order */
3412                         if (++serv->attr.ref)
3413                             serv->attr.flags |= SERV_ENABLED;
3414                     }
3415                 } else {
3416                     if (del && this) {
3417                         xremove(dfd, clink);            /* Found it, remove link */
3418                         if (--serv->attr.ref <= 0)
3419                             serv->attr.flags &= ~SERV_ENABLED;
3420                     }
3421                 }
3422             }
3423
3424             if (this) {
3425                 /*
3426                  * If we haven't found it and we shouldn't delete it
3427                  * we try to add it.
3428                  */
3429                 if (!del && !found) {
3430                     xsymlink(dfd, olink, nlink);
3431                     if (++serv->attr.ref)
3432                         serv->attr.flags |= SERV_ENABLED;
3433                     found = true;
3434                 }
3435             }
3436         }
3437
3438         popd();
3439         closedir(rcdir);
3440     }
3441 # endif /* !SUSE, standard SystemV link scheme */
3442 #endif  /* !DEBUG */
3443
3444     /*
3445      * Do the makedep
3446      */
3447     makedep();
3448
3449     /*
3450      * Back to the root(s)
3451      */
3452     popd();
3453
3454     /*
3455      * Make valgrind happy
3456      */
3457     if (path != ipath) free(path);
3458     if (root) free(root);
3459
3460     return 0;
3461 }