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
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.
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 */
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/syscall.h>
37 #if defined(USE_RPMLIB) && (USE_RPMLIB > 0)
38 # include <rpm/rpmlib.h>
39 # include <rpm/rpmmacro.h>
40 #endif /* USE_RPMLIB */
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)
52 while ((ptr = strpbrk(ptr, "016sS")))
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 */
64 # define INITDIR "/etc/init.d"
67 # define OVERRIDEDIR "/etc/insserv/overrides"
70 # define INSCONF "/etc/insserv.conf"
74 * For a description of regular expressions see regex(7).
76 #define COMM "^#[[:blank:]]*"
77 #define VALUE ":[[:blank:]]*([[:print:]]*)"
78 /* The second substring contains our value (the first is all) */
81 #define START "[-_]+start"
82 #define STOP "[-_]+stop"
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
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:]]*)"
108 /* The root file system */
111 /* The main line buffer if unique */
112 static char buf[LINE_MAX];
114 /* When to be verbose */
115 static boolean verbose = false;
117 /* When to be verbose */
118 static boolean dryrun = false;
120 /* When paths set do not add root if any */
121 static boolean set_override = false;
122 static boolean set_insconf = false;
124 /* Search results points here */
125 typedef struct lsb_struct {
127 char *required_start;
136 } attribute((aligned(sizeof(char*)))) lsb_t;
138 /* Search results points here */
139 typedef struct reg_struct {
150 } attribute((aligned(sizeof(regex_t)))) reg_t;
152 typedef struct creg_struct {
155 } attribute((aligned(sizeof(regex_t)))) creg_t;
157 static lsb_t script_inf;
160 static char empty[1] = "";
162 /* Delimeters used for spliting results with strsep(3) */
163 const char *const delimeter = " ,;\t";
166 * push and pop directory changes: pushd() and popd()
168 typedef struct pwd_struct {
172 #define getpwd(list) list_entry((list), struct pwd_struct, deep)
174 static list_t pwd = { &pwd, &pwd }, * topd = &pwd;
176 static void pushd(const char *restrict const path) attribute((nonnull(1)));
177 static void pushd(const char *restrict const path)
181 if (posix_memalign((void*)&dir, sizeof(void*), alignof(pwd_t)) == 0) {
182 if (!(dir->pwd = getcwd((char*)0, 0)))
184 insert(&dir->deep, topd->prev);
190 error ("pushd() can not change to directory %s: %s\n", path, strerror(errno));
193 static void popd(void)
197 if (list_empty(topd))
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));
210 * Linked list of system facilities services and their replacment
212 typedef struct string {
217 typedef struct repl {
221 #define getrepl(arg) list_entry((arg), struct repl, r_list)
223 typedef struct faci {
228 #define getfaci(arg) list_entry((arg), struct faci, list)
230 static list_t sysfaci = { &sysfaci, &sysfaci }, *sysfaci_start = &sysfaci;
233 * Remember requests for required or should services and expand `$' token
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)
239 const char type = (bit & REQ_KILL) ? 'K' : 'S';
241 char * tmp = strdupa(required);
242 list_t * ptr, * list;
246 error("%s", strerror(errno));
248 while ((token = strsep(&tmp, delimeter)) && *token) {
249 service_t * req, * here, * need;
250 boolean found = false;
256 /* This is an optional token */
261 req = addservice(token);
262 if (bit & REQ_KILL) {
264 list = &req->sort.rev;
268 serv = getorig(serv);
269 list = &serv->sort.req;
273 np_list_for_each(ptr, list) {
274 if (!strcmp(getreq(ptr)->serv->name, need->name)) {
275 getreq(ptr)->flags |= bit;
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);
289 /* Expand requested services for sorting */
290 requires(here, need, type);
293 if (strcasecmp(token, "$all") == 0) {
294 serv->attr.flags |= SERV_ALL;
297 /* Expand the `$' token recursively down */
298 list_for_each(ptr, sysfaci_start) {
299 if (!strcmp(token, getfaci(ptr)->name)) {
301 np_list_for_each(lst, &getfaci(ptr)->replace)
302 rememberreq(serv, bit, getrepl(lst)->r[0].name);
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)
316 char * tmp = strdupa(list);
320 error("%s", strerror(errno));
322 while ((token = strsep(&tmp, delimeter)) && *token) {
334 rev = addservice(token);
335 rememberreq(rev, bit, serv->name);
338 list_for_each(ptr, sysfaci_start) {
339 if (!strcmp(token, getfaci(ptr)->name)) {
341 np_list_for_each(lst, &getfaci(ptr)->replace)
342 reversereq(serv, bit, getrepl(lst)->r[0].name);
352 * Check required services for name
354 static boolean chkrequired(service_t *restrict serv) attribute((nonnull(1)));
355 static boolean chkrequired(service_t *restrict serv)
362 serv = getorig(serv);
364 np_list_for_each(pos, &serv->sort.req) {
365 req_t *req = getreq(pos);
368 if ((req->flags & REQ_MUST) == 0)
371 must = getorig(must);
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);
380 if (serv->attr.flags & (SERV_CMDLINE|SERV_ENABLED))
382 np_list_for_each(pos, &serv->sort.rev) {
383 req_t *rev = getreq(pos);
386 if ((rev->flags & REQ_MUST) == 0)
389 must = getorig(must);
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);
403 * Check dependencies for name as a service
405 static boolean chkdependencies(service_t *restrict serv) attribute((nonnull(1)));
406 static boolean chkdependencies(service_t *restrict serv)
408 const char * const name = serv->name;
412 list_for_each(ptr, s_start) {
413 service_t * cur = getservice(ptr);
419 if ((cur->attr.flags & SERV_ENABLED) == 0)
422 if (cur->attr.flags & SERV_DUPLET)
425 if (list_empty(&cur->sort.req))
428 np_list_for_each(pos, &cur->sort.req) {
429 req_t *req = getreq(pos);
430 const ushort flags = req->serv->attr.flags;
432 if (!(req->flags & REQ_MUST))
435 if (strcmp(req->serv->name, name) != 0)
438 if ((cur->attr.flags & SERV_CMDLINE) && (flags & SERV_CMDLINE))
441 warn("Service %s has to be enabled to start service %s\n",
450 * This helps us to work out the current symbolic link structure
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)
457 service_t * serv = addservice(this);
459 ushort here = map_runlevel_to_lvl(runlvl);
463 if (!serv->attr.korder)
464 serv->attr.korder = 99;
465 if (serv->attr.korder > order)
466 serv->attr.korder = order;
468 /* This because SuSE boot script concept uses a differential link scheme. */
473 if (serv->attr.sorder < order)
474 serv->attr.sorder = order;
481 static void setlsb(const char *restrict const name) attribute((unused));
482 static void setlsb(const char *restrict const name)
484 service_t * serv = findservice(name);
486 serv->attr.flags &= ~SERV_NOTLSB;
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.
494 static inline void nonlsb_script(void) attribute((always_inline));
495 static inline void nonlsb_script(void)
499 list_for_each(pos, s_start) {
500 if (getservice(pos)->attr.flags & SERV_NOTLSB) {
501 service_t * req, * srv = getservice(pos);
507 list_for_each(tmp, s_start) {
508 service_t * cur = getservice(tmp);
509 if (cur->attr.flags & SERV_NOTLSB)
511 if ((cur->attr.flags & SERV_ENABLED) == 0)
513 if (!cur->attr.sorder)
515 if ((srv->start->lvl & cur->start->lvl) == 0)
517 if (cur->attr.sorder >= srv->attr.sorder)
519 if (max < cur->attr.sorder) {
520 max = cur->attr.sorder;
526 requires(srv, req, 'S');
530 list_for_each(tmp, s_start) {
531 service_t * cur = getservice(tmp);
532 if (cur->attr.flags & SERV_NOTLSB)
534 if ((cur->attr.flags & SERV_ENABLED) == 0)
536 if (!cur->attr.korder)
538 if ((srv->stopp->lvl & cur->stopp->lvl) == 0)
540 if (cur->attr.korder <= srv->attr.korder)
542 if (max > cur->attr.korder) {
543 max = cur->attr.korder;
549 requires(req, srv, 'K');
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.
559 static inline void active_script(void) attribute((always_inline));
560 static inline void active_script(void)
565 for (deep = 0; deep < 100; deep++) {
566 list_for_each(pos, s_start) {
567 service_t * serv = getservice(pos);
570 if (serv->attr.script == (char*)0)
573 if ((serv->attr.flags & SERV_INTRACT) == 0)
576 serv->attr.sorder = getorder(serv->attr.script, 'S');
578 if (serv->attr.sorder != deep)
581 if (serv->attr.flags & SERV_DUPLET)
582 continue; /* Duplet */
584 list_for_each(tmp, s_start) {
585 service_t * cur = getservice(tmp);
588 if (getorig(cur) == serv)
591 if ((serv->start->lvl & cur->start->lvl) == 0)
595 * Use real script name for getorder()/setorder()
597 if (cur->attr.script == (char*)0)
599 script = cur->attr.script;
601 cur->attr.sorder = getorder(script, 'S');
603 if (cur->attr.sorder != deep)
606 * Increase order of members of the same start
607 * group and recalculate dependency order (`true')
609 setorder(script, 'S', ++cur->attr.sorder, true);
616 * Last but not least the `$all' scripts will be set to the
617 * end of the current start order.
619 static inline void all_script(void) attribute((always_inline));
620 static inline void all_script(void)
624 list_for_each(pos, s_start) {
625 service_t * serv = getservice(pos);
629 if (serv->attr.flags & SERV_DUPLET)
630 continue; /* Duplet */
632 if (!(serv->attr.flags & SERV_ALL))
635 if (serv->attr.script == (char*)0)
639 list_for_each(tmp, s_start) {
640 service_t * cur = getservice(tmp);
642 if (cur->attr.flags & SERV_DUPLET)
643 continue; /* Duplet */
645 if ((serv->start->lvl & cur->start->lvl) == 0)
648 if (cur->attr.script == (char*)0)
654 if (cur->attr.flags & SERV_ALL)
657 cur->attr.sorder = getorder(cur->attr.script, 'S');
659 if (cur->attr.sorder > neworder)
660 neworder = cur->attr.sorder;
664 if (neworder > MAX_DEEP)
666 else if (neworder > maxstart)
669 setorder(serv->attr.script, 'S', neworder, false);
674 * Make the dependency files
676 static inline void makedep(void) attribute((always_inline));
677 static inline void makedep(void)
679 FILE *boot, *start, *stop, *out;
680 #ifdef USE_KILL_IN_BOOT
682 #endif /* USE_KILL_IN_BOOT */
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 */
694 if (!(boot = fopen(".depend.boot", "w"))) {
695 warn("fopen(.depend.stop): %s\n", strerror(errno));
699 if (!(start = fopen(".depend.start", "w"))) {
700 warn("fopen(.depend.start): %s\n", strerror(errno));
705 info("creating .depend.boot\n");
706 info("creating .depend.start\n");
708 lsort('S'); /* Sort into start order, set new sorder */
711 fprintf(boot, "TARGETS =");
712 while ((serv = listscripts(&target, 'S', LVL_BOOT))) {
715 #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
716 if (serv->attr.ref <= 0)
718 #endif /* MINIMAL_MAKE */
719 fprintf(boot, " %s", target);
724 fprintf(start, "TARGETS =");
725 while ((serv = listscripts(&target, 'S', LVL_ALL))) { /* LVL_ALL: nearly all but not BOOT */
728 #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
729 if (serv->attr.ref <= 0)
731 #endif /* MINIMAL_MAKE */
732 fprintf(start, " %s", target);
736 fprintf(boot, "INTERACTIVE =");
737 fprintf(start, "INTERACTIVE =");
740 while ((serv = listscripts(&target, 'S', LVL_BOOT|LVL_ALL))) {
743 #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
744 if (serv->attr.ref <= 0)
746 #endif /* not MINIMAL_MAKE */
748 if (list_empty(&serv->sort.req))
751 if (serv->start->lvl & LVL_BOOT)
756 if (serv->attr.flags & SERV_INTRACT)
757 fprintf(out, " %s", target);
763 while ((serv = listscripts(&target, 'S', LVL_BOOT|LVL_ALL))) {
769 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
770 if (serv->attr.ref <= 0)
772 #endif /* not MINIMAL_RULES */
774 if (list_empty(&serv->sort.req))
777 if (serv->start->lvl & LVL_BOOT)
783 if (serv->attr.flags & SERV_ALL) {
784 list_for_each(pos, s_start) {
785 service_t * dep = getservice(pos);
791 if (dep->attr.flags & SERV_DUPLET)
792 continue; /* Duplet */
794 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
795 if (dep->attr.ref <= 0)
797 #endif /* not MINIMAL_RULES */
800 * No self dependcies or from the last
802 if (dep == serv || (dep->attr.flags & SERV_ALL))
805 if ((serv->start->lvl & dep->start->lvl) == 0)
808 if ((name = dep->attr.script) == (char*)0)
812 fprintf(out, "%s:", target);
815 fprintf(out, " %s", name);
818 np_list_for_each(pos, &serv->sort.req) {
819 req_t * req = getreq(pos);
820 service_t * dep = req->serv;
826 if (dep->attr.flags & SERV_DUPLET)
829 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
830 if (dep->attr.ref <= 0)
832 #endif /* not MINIMAL_RULES */
835 * No self dependcies or from the last
837 if (dep == serv || (dep->attr.flags & SERV_ALL))
840 if ((serv->start->lvl & dep->start->lvl) == 0)
843 if ((name = dep->attr.script) == (char*)0)
847 fprintf(out, "%s:", target);
850 fprintf(out, " %s", name);
853 if (mark) fputc('\n', out);
859 if (!(stop = fopen(".depend.stop", "w"))) {
860 warn("fopen(.depend.stop): %s\n", strerror(errno));
864 #ifdef USE_KILL_IN_BOOT
865 if (!(halt = fopen(".depend.halt", "w"))) {
866 warn("fopen(.depend.start): %s\n", strerror(errno));
871 info("creating .depend.halt\n");
872 #endif /* USE_KILL_IN_BOOT */
873 info("creating .depend.stop\n");
875 lsort('K'); /* Sort into stop order, set new korder */
878 fprintf(stop, "TARGETS =");
879 while ((serv = listscripts(&target, 'K', LVL_NORM))) { /* LVL_NORM: nearly all but not BOOT and not SINGLE */
882 #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
883 if (serv->attr.ref <= 0)
885 #endif /* MINIMAL_MAKE */
886 fprintf(stop, " %s", target);
890 #ifdef USE_KILL_IN_BOOT
892 fprintf(halt, "TARGETS =");
893 while ((serv = listscripts(&target, 'K', LVL_BOOT))) {
896 # if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0)
897 if (serv->attr.ref <= 0)
899 # endif /* MINIMAL_MAKE */
900 fprintf(halt, " %s", target);
903 #endif /* USE_KILL_IN_BOOT */
906 while ((serv = listscripts(&target, 'K', (LVL_NORM|LVL_BOOT)))) {
912 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
913 if (serv->attr.ref <= 0)
915 #endif /* not MINIMAL_RULES */
917 if (list_empty(&serv->sort.rev))
920 if (serv->stopp->lvl & LVL_BOOT)
921 #ifdef USE_KILL_IN_BOOT
924 #else /* not USE_KILL_IN_BOOT */
926 #endif /* not USE_KILL_IN_BOOT */
930 np_list_for_each(pos, &serv->sort.rev) {
931 req_t * rev = getreq(pos);
932 service_t * dep = rev->serv;
938 if (dep->attr.flags & (SERV_DUPLET|SERV_NOSTOP))
939 continue; /* Duplet or no stop link */
941 #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0)
942 if (dep->attr.ref <= 0)
944 #endif /* not MINIMAL_RULES */
946 if ((serv->stopp->lvl & dep->stopp->lvl) == 0)
949 if ((name = dep->attr.script) == (char*)0)
953 fprintf(out, "%s:", target);
956 fprintf(out, " %s", name);
958 if (mark) fputc('\n', out);
961 #ifdef USE_KILL_IN_BOOT
963 #endif /* USE_KILL_IN_BOOT */
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)
974 extension char buf[strlen(myname)+2+strlen(fmt)+1];
975 strcat(strcat(strcpy(buf, myname), ": "), fmt);
976 vfprintf(stderr, buf, ap);
983 void error (const char *restrict const fmt, ...)
999 void warn (const char *restrict const fmt, ...)
1009 * Print message when verbose is enabled
1011 void info(const char *fmt, ...) {
1023 * Check for script in list.
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)
1030 boolean isinc = false;
1031 register int c = cnt;
1035 if (*script != *list[c])
1037 if (!strcmp(script, list[c])) {
1047 * Open a runlevel directory, if it not
1048 * exists than create one.
1050 static DIR * openrcdir(const char *restrict const rcpath) attribute((nonnull(1)));
1051 static DIR * openrcdir(const char *restrict const rcpath)
1057 if (stat(rcpath, &st) < 0) {
1058 if (errno == ENOENT) {
1059 info("creating directory '%s'\n", rcpath);
1061 mkdir(rcpath, (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH));
1063 error("can not stat(%s): %s\n", rcpath, strerror(errno));
1066 if ((rcdir = opendir(rcpath)) == (DIR*)0) {
1068 warn ("can not opendir(%s): %s\n", rcpath, strerror(errno));
1070 error("can not opendir(%s): %s\n", rcpath, strerror(errno));
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);
1082 * Wrapper for regcomp(3)
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)
1089 register int ret = regcomp(preg, regex, cflags);
1091 regerror(ret, preg, buf, sizeof (buf));
1099 * Wrapper for regexec(3)
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)
1107 register int ret = regexec(preg, string, nmatch, pmatch, eflags);
1108 if (ret > REG_NOMATCH) {
1109 regerror(ret, preg, buf, sizeof (buf));
1113 return (ret ? false : true);
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.
1122 static inline void scan_script_regalloc(void) attribute((always_inline));
1123 static inline void scan_script_regalloc(void)
1125 regcompiler(®.prov, PROVIDES, REG_EXTENDED|REG_ICASE);
1126 regcompiler(®.req_start, REQUIRED_START, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1127 regcompiler(®.req_stop, REQUIRED_STOP, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1128 regcompiler(®.shl_start, SHOULD_START, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1129 regcompiler(®.shl_stop, SHOULD_STOP, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1130 regcompiler(®.start_bf, START_BEFORE, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1131 regcompiler(®.stop_af, STOP_AFTER, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1132 regcompiler(®.def_start, DEFAULT_START, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1133 regcompiler(®.def_stop, DEFAULT_STOP, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1134 regcompiler(®.desc, DESCRIPTION, REG_EXTENDED|REG_ICASE|REG_NEWLINE);
1137 static inline void scan_script_reset(void) attribute((always_inline));
1138 static inline void scan_script_reset(void)
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);
1152 #define FOUND_LSB_HEADER 0x01
1153 #define FOUND_LSB_DEFAULT 0x02
1154 #define FOUND_LSB_OVERRIDE 0x04
1156 static int o_flags = O_RDONLY;
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)
1163 regmatch_t subloc[SUBNUM_SHD+1], *val = &subloc[SUBNUM-1], *shl = &subloc[SUBNUM_SHD-1];
1164 char *begin = (char*)0, *end = (char*)0;
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
1181 info("Loading %s\n", path);
1183 if ((fd = xopen(dfd, path, o_flags)) < 0 || (script = fdopen(fd, "r")) == (FILE*)0)
1184 error("fopen(%s): %s\n", path, strerror(errno));
1186 #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
1187 (void)posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
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)) {
1194 /* Skip scanning above from LSB magic start */
1196 if ( (begin = strstr(buf, "### BEGIN INIT INFO")) ) {
1197 /* Let the latest LSB header override the one found earlier */
1198 scan_script_reset();
1203 if (!provides && regexecutor(®.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);
1210 if (!required_start && regexecutor(®.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);
1215 required_start = empty;
1217 if (!required_stop && regexecutor(®.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);
1222 required_stop = empty;
1224 if (!should_start && regexecutor(®.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);
1229 should_start = empty;
1231 if (!should_stop && regexecutor(®.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);
1236 should_stop = empty;
1238 if (!start_before && regexecutor(®.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);
1243 start_before = empty;
1245 if (!stop_after && regexecutor(®.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);
1252 if (!default_start && regexecutor(®.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);
1257 default_start = empty;
1260 if (!default_stop && regexecutor(®.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);
1265 default_stop = empty;
1268 if (!description && regexecutor(®.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);
1273 description = empty;
1276 /* Skip scanning below from LSB magic end */
1277 if ((end = strstr(buf, "### END INIT INFO")))
1281 #undef COMMON_SHD_ARGS
1283 #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600
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);
1289 (void)posix_fadvise(fd, 0, 0, POSIX_FADV_NOREUSE);
1295 ret |= FOUND_LSB_HEADER;
1297 if (begin && !end) {
1298 char *name = basename(path);
1299 if (*name == 'S' || *name == 'K')
1301 warn("Script %s is broken: missing end of LSB comment.\n", name);
1303 error("exiting now!\n");
1306 if (begin && end && (!provides || (provides == empty) ||
1308 !required_start || !required_stop || !default_start
1309 #else /* not SUSE */
1310 !required_start || !required_stop || !default_start || !default_stop
1311 #endif /* not SUSE */
1314 char *name = basename(path);
1315 if (*name == 'S' || *name == 'K')
1317 warn("Script %s is broken: incomplete LSB comment.\n", name);
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");
1325 warn("missing `Required-Stop:' entry: please add even if empty.\n");
1327 warn("missing `Default-Start:' entry: please add even if empty.\n");
1330 warn("missing `Default-Stop:' entry: please add even if empty.\n");
1335 #undef required_start
1336 #undef required_stop
1341 #undef default_start
1348 * Follow symlinks, return the basename of the file pointed to by
1349 * symlinks or the basename of the current path if no symlink.
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)
1355 char linkbuf[PATH_MAX+1];
1356 char *script = xstrdup(path);
1358 strncpy(linkbuf, script, sizeof(linkbuf)-1);
1359 linkbuf[PATH_MAX] = '\0';
1365 if (deep++ > MAXSYMLINKS) {
1367 warn("Can not determine script name for %s: %s\n", path, strerror(errno));
1371 if (xlstat(dfd, script, &st) < 0) {
1372 warn("Can not stat %s: %s\n", script, strerror(errno));
1376 if (!S_ISLNK(st.st_mode))
1379 if ((linklen = xreadlink(dfd, script, linkbuf, sizeof(linkbuf)-1)) < 0)
1381 linkbuf[linklen] = '\0';
1383 if (linkbuf[0] != '/') { /* restore relative links */
1384 const char *lastslash;
1386 if ((lastslash = strrchr(script, '/'))) {
1387 size_t dirname_len = lastslash - script + 1;
1389 if (dirname_len + linklen > PATH_MAX)
1390 linklen = PATH_MAX - dirname_len;
1392 memmove(&linkbuf[dirname_len], &linkbuf[0], linklen + 1);
1393 memcpy(&linkbuf[0], script, dirname_len);
1398 script = xstrdup(linkbuf);
1400 if (deep == 1 && first)
1401 *first = xstrdup(basename(linkbuf));
1406 script = xstrdup(basename(linkbuf));
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)
1419 char fullpath[PATH_MAX+1];
1420 struct stat statbuf;
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));
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;
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)
1443 char * name = scriptname(dfd, path, first);
1449 /* Reset old results */
1450 scan_script_reset();
1453 /* Common script ... */
1454 if (!strcmp(name, "halt")) {
1455 ret |= (FOUND_LSB_HEADER|FOUND_LSB_DEFAULT);
1459 /* ... and its link */
1460 if (!strcmp(name, "reboot")) {
1461 ret |= (FOUND_LSB_HEADER|FOUND_LSB_DEFAULT);
1465 /* Common script for single mode */
1466 if (!strcmp(name, "single")) {
1467 ret |= (FOUND_LSB_HEADER|FOUND_LSB_DEFAULT);
1472 /* Replace with headers from the script itself */
1473 ret |= scan_lsb_headers(dfd, path, cache, ignore);
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);
1479 ret |= FOUND_LSB_DEFAULT;
1482 * Allow host-specific overrides to replace the content in the
1485 ret |= load_overrides(override_path, name, cache, ignore);
1493 static inline void scan_script_regfree() attribute((always_inline));
1494 static inline void scan_script_regfree()
1497 regfree(®.req_start);
1498 regfree(®.req_stop);
1499 regfree(®.shl_start);
1500 regfree(®.shl_stop);
1501 regfree(®.start_bf);
1502 regfree(®.stop_af);
1503 regfree(®.def_start);
1504 regfree(®.def_stop);
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 */
1537 #define RUNLEVLES (int)(sizeof(runlevel_locations)/sizeof(runlevel_locations[0]))
1539 int map_has_runlevels(void)
1544 char map_runlevel_to_key(const int runlevel)
1546 if (runlevel >= RUNLEVLES) {
1547 warn("Wrong runlevel %d\n", runlevel);
1549 return runlevel_locations[runlevel].key;
1552 ushort map_key_to_lvl(const char key)
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;
1560 warn("Wrong runlevel key '%c'\n", uckey);
1564 const char *map_runlevel_to_location(const int runlevel)
1566 if (runlevel >= RUNLEVLES) {
1567 warn("Wrong runlevel %d\n", runlevel);
1569 return runlevel_locations[runlevel].location;
1572 ushort map_runlevel_to_lvl(const int runlevel)
1574 if (runlevel >= RUNLEVLES) {
1575 warn("Wrong runlevel %d\n", runlevel);
1577 return runlevel_locations[runlevel].lvl;
1580 ushort map_runlevel_to_seek(const int runlevel)
1582 return runlevel_locations[runlevel].seek;
1586 * Two helpers for runlevel bits and strings.
1588 ushort str2lvl(const char *restrict lvl)
1590 char * token, *tmp = strdupa(lvl);
1594 error("%s", strerror(errno));
1596 while ((token = strsep(&tmp, delimeter))) {
1597 if (!*token || strlen(token) != 1)
1599 if (!strpbrk(token, "0123456sSbB"))
1602 ret |= map_key_to_lvl(*token);
1608 char * lvl2str(const ushort lvl)
1615 last = ptr = &str[0];
1616 memset(ptr, '\0', sizeof(str));
1617 for (num = 0; num < RUNLEVLES; num++) {
1625 else if (LVL_SINGLE & bit)
1627 else if (LVL_BOOT & bit)
1629 #else /* not SUSE */
1630 else if (LVL_BOOT & bit)
1632 #endif /* not SUSE */
1634 error("Wrong runlevel %d\n", num);
1638 if (strlen(str) == 0)
1640 return xstrdup(str);
1644 * Scan current service structure
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)
1655 for (runlevel = 0; runlevel < RUNLEVLES; runlevel++) {
1656 const char * rcd = (char*)0;
1657 struct stat st_script;
1663 rcd = map_runlevel_to_location(runlevel);
1665 rcdir = openrcdir(rcd); /* Creates runlevel directory if necessary */
1666 if (rcdir == (DIR*)0)
1668 if ((dfd = dirfd(rcdir)) < 0) {
1674 while ((d = readdir(rcdir)) != (struct dirent*)0) {
1675 char * name = (char *)0;
1676 char * ptr = d->d_name;
1678 char * begin; /* Remember address of ptr handled by strsep() */
1683 if (*ptr != 'S' && *ptr != 'K')
1688 if (strspn(ptr, "0123456789") < 2)
1693 if (xstat(dfd, d->d_name, &st_script) < 0) {
1694 xremove(dfd, d->d_name); /* dangling sym link */
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);
1704 script_inf.required_start = xstrdup(DEFAULT_DEPENDENCY);
1705 script_inf.required_stop = xstrdup(DEFAULT_DEPENDENCY);
1707 #endif /* not SUSE */
1709 first = (service_t*)0;
1710 begin = script_inf.provides;
1711 while ((token = strsep(&begin, delimeter)) && *token) {
1712 service_t * service;
1714 if (*token == '$') {
1715 warn("script %s provides system facility %s, skipped!\n", d->d_name, token);
1718 if (*token == '#') {
1719 warn("script %s provides facility %s with comment sign, skipped!\n", d->d_name, token);
1723 service = current_structure(token, order, runlevel, type);
1726 nickservice(first, service);
1730 if (!makeprov(service, name))
1733 ++service->attr.ref; /* May enabled in several levels */
1735 if (service->attr.flags & SERV_KNOWN)
1737 service->attr.flags |= (SERV_KNOWN|SERV_ENABLED);
1740 service->attr.flags |= SERV_NOTLSB;
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);
1746 warn("warning: script '%s' missing LSB tags\n", d->d_name);
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 */
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 */
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 */
1770 if (script_inf.required_stop && script_inf.required_stop != empty) {
1771 rememberreq(service, REQ_MUST|REQ_KILL, script_inf.required_stop);
1773 if (script_inf.should_stop && script_inf.should_stop != empty) {
1774 rememberreq(service, REQ_SHLD|REQ_KILL, script_inf.should_stop);
1776 if (script_inf.stop_after && script_inf.stop_after != empty) {
1777 reversereq(service, REQ_SHLD|REQ_KILL, script_inf.stop_after);
1784 scan_script_reset();
1786 } /* while ((token = strsep(&begin, delimeter)) && *token) */
1796 * The /etc/insserv.conf scanning engine.
1798 static void scan_conf_file(const char *restrict file) attribute((nonnull(1)));
1799 static void scan_conf_file(const char *restrict file)
1801 regmatch_t subloc[SUBCONFNUM], *val = (regmatch_t*)0;
1804 info("Loading %s\n", file);
1807 const char * fptr = file;
1810 /* Try relativ location first */
1811 if ((conf = fopen(fptr, "r")))
1813 /* Try absolute location */
1814 if ((conf = fopen(file, "r")))
1819 while (fgets(buf, sizeof(buf), conf)) {
1820 char *pbuf = &buf[0];
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;
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;
1839 boolean found = false;
1840 list_for_each(ptr, sysfaci_start) {
1841 if (!strcmp(getfaci(ptr)->name, virt)) {
1844 list_t * r_list = &getfaci(ptr)->replace;
1846 while ((token = strsep(&real, delimeter))) {
1847 repl_t *restrict subst;
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);
1853 if (posix_memalign((void*)&r->ref, sizeof(void*), alignof(typeof(r->ref))+strsize(token)) != 0)
1854 error("%s", strerror(errno));
1856 r->name = ((char*)(r->ref))+alignof(typeof(r->ref));
1857 strcpy(r->name, token);
1864 faci_t *restrict this;
1865 if (posix_memalign((void*)&this, sizeof(void*), alignof(faci_t)) != 0)
1866 error("%s", strerror(errno));
1868 list_t * r_list = &this->replace;
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;
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);
1881 if (posix_memalign((void*)&r->ref, sizeof(void*), alignof(typeof(r->ref))+strsize(token)) != 0)
1882 error("%s", strerror(errno));
1884 r->name = ((char*)(r->ref))+alignof(typeof(r->ref));
1885 strcpy(r->name, token);
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;
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;
1903 if (key && *key == '<' && servs && *servs) {
1904 if (!strncmp("<interactive>", key, strlen(key))) {
1906 while ((token = strsep(&servs, delimeter))) {
1907 service_t *service = addservice(token);
1908 service = getorig(service);
1909 service->attr.flags |= SERV_INTRACT;
1919 warn("fopen(%s): %s\n", file, strerror(errno));
1922 static int cfgfile_filter(const struct dirent *restrict d) attribute((nonnull(1)));
1923 static int cfgfile_filter(const struct dirent *restrict d)
1925 boolean ret = false;
1926 const char * name = d->d_name;
1931 if (!name || (*name == '\0'))
1933 if ((end = strrchr(name, '.'))) {
1935 if (!strncmp(end, "rpm", 3) || /* .rpmorig, .rpmnew, .rmpsave, ... */
1936 !strncmp(end, "ba", 2) || /* .bak, .backup, ... */
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 */
1952 if ((end = strrchr(name, ','))) {
1954 if (!strcmp(end, "v")) /* rcs-files */
1962 static void scan_conf(const char *restrict file) attribute((nonnull(1)));
1963 static void scan_conf(const char *restrict file)
1965 struct dirent** namelist = (struct dirent**)0;
1966 char path[PATH_MAX+1];
1969 regcompiler(&creg.isysfaci, CONFLINE, REG_EXTENDED|REG_ICASE);
1970 regcompiler(&creg.isactive, CONFLINE2, REG_EXTENDED|REG_ICASE);
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));
1976 scan_conf_file(path);
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));
1982 n = scandir(path, &namelist, cfgfile_filter, alphasort);
1986 char buf[PATH_MAX+1];
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));
1993 if ((stat(buf, &st) < 0) || !S_ISREG(st.st_mode))
1996 scan_conf_file(buf);
2005 regfree(&creg.isysfaci);
2006 regfree(&creg.isactive);
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)
2013 repl_t * rent = getrepl(rlist);
2014 list_t * tmp, * safe, * ptr = (list_t*)0;
2016 list_for_each(tmp, sysfaci_start) {
2017 if (!strcmp(getfaci(tmp)->name, rent->r[0].name)) {
2018 ptr = &getfaci(tmp)->replace;
2023 if (!ptr || list_empty(ptr)) {
2025 if (--(*rent->r[0].ref) <= 0)
2026 free(rent->r[0].ref);
2031 if ((*deep)++ > 10) {
2032 warn("The nested level of the system facilities in the insserv.conf file(s) is to large\n");
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);
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);
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);
2061 static inline void expand_conf(void)
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 == '$') {
2069 expand_faci(rlist, rlist, &deep);
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.
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)
2087 char * ret = (char*)0;
2089 while ((d = readdir(rcdir)) != (struct dirent*)0) {
2090 char * ptr = d->d_name;
2096 if (strspn(ptr, "0123456789") < 2)
2100 if (!strcmp(ptr, script)) {
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.
2114 static inline boolean underrpm(void)
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 */
2125 snprintf(buf, sizeof(buf)-1, "/proc/%lu/cmdline", (unsigned long)pp);
2126 if ((fd = open(buf, O_NOCTTY|O_RDONLY)) < 0)
2129 memset(buf, '\0', sizeof(buf));
2130 if ((len = read(fd , buf, sizeof(buf)-1)) < 0)
2139 if ((len = len - (ssize_t)(ptr - &buf[0])) < 0)
2141 } while ((ptr = memchr(ptr, '\0', len)) && *(++ptr));
2146 # if defined(USE_RPMLIB) && (USE_RPMLIB > 0)
2147 rpmReadConfigFiles(NULL, NULL);
2150 if ((shell = rpmExpand("%_buildshell", NULL)) == NULL)
2151 shell = xstrdup("/bin/sh");
2153 if (strncmp(argv[0], shell, strlen(shell)) != 0) {
2159 if ((tmppath = rpmExpand("%_tmppath", NULL)) == NULL)
2160 tmppath = xstrdup("/var/tmp");
2162 if (strncmp(argv[1], tmppath, strlen(tmppath)) != 0) {
2167 len = strlen(tmppath);
2171 if (strncmp(ptr + len, "/rpm-tmp.", 9) != 0)
2173 # else /* not USE_RPMLIB */
2174 if ((strcmp(argv[0], "/bin/sh") != 0) &&
2175 (strcmp(argv[0], "/bin/bash") != 0))
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))
2182 # endif /* not USE_RPMLIB */
2183 if ((argc = atoi(argv[2])) >= 0 && argc <= 2)
2193 static struct option long_options[] =
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 },
2207 static void help(const char *restrict const name) attribute((nonnull(1)));
2208 static void help(const char *restrict const name)
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");
2227 int main (int argc, char *argv[])
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;
2242 myname = basename(*argv);
2249 if (getuid() == (uid_t)0)
2250 o_flags |= O_NOATIME;
2252 for (c = 0; c < argc; c++)
2255 while ((c = getopt_long(argc, argv, "c:dfrhvno:p:", long_options, (int *)0)) != -1) {
2259 if (optarg == (char*)0 || *optarg == '\0')
2281 if (optarg == (char*)0 || *optarg == '\0')
2283 if (path != ipath) free(path);
2284 l = strlen(optarg) - 1;
2285 path = xstrdup(optarg);
2286 if (*(path+l) == '/')
2290 if (optarg == (char*)0 || *optarg == '\0')
2292 override_path = optarg;
2293 set_override = true;
2297 error("For help use: %s -h\n", myname);
2309 error("usage: %s [[-r] init_script|init_directory]\n", myname);
2312 char * token = strpbrk(*argv, delimeter);
2315 * Let us separate the script/service name from the additional arguments.
2317 if (token && *token) {
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));
2328 if (stat(*argv, &st_script) < 0)
2329 error("%s: %s\n", *argv, strerror(errno));
2333 if (S_ISDIR(st_script.st_mode)) {
2334 const size_t l = strlen(*argv) - 1;
2336 if (path != ipath) free(path);
2337 path = xstrdup(*argv);
2338 if (*(path+l) == '/')
2344 error("usage: %s [[-r] init_script|init_directory]\n", myname);
2347 char * base, * ptr = xstrdup(*argv);
2349 if ((base = strrchr(ptr, '/'))) {
2350 if (path != ipath) free(path);
2358 if (strcmp(path, INITDIR) != 0) {
2360 root = xstrdup(path);
2361 if ((tmp = strstr(root, INITDIR))) {
2372 char * token = strpbrk(argv[c], delimeter);
2375 * Let us separate the script/service name from the additional arguments.
2377 if (token && *token) {
2382 if (stat(argv[c], &st_script) < 0) {
2383 if (errno != ENOENT)
2384 error("%s: %s\n", argv[c], strerror(errno));
2386 if (stat(argv[c], &st_script) < 0)
2387 error("%s: %s\n", argv[c], strerror(errno));
2390 if ((base = strrchr(argv[c], '/'))) {
2396 #if defined(DEBUG) && (DEBUG > 0)
2397 for (c = 0; c < argc; c++)
2399 printf("Overwrite argument for %s is %s\n", argv[c], argr[c]);
2403 * Scan and set our configuration for virtual services.
2408 * Expand system facilities to real serivces
2413 * Initialize the regular scanner for the scripts.
2415 scan_script_regalloc();
2418 * Scan always for the runlevel links to see the current
2419 * link scheme of the services.
2421 scan_script_locations(path, override_path, ignore);
2424 * Clear out aliases found for scripts found up to this point.
2429 * Open the script directory
2431 if ((initdir = opendir(path)) == (DIR*)0 || (dfd = dirfd(initdir)) < 0)
2432 error("can not opendir(%s): %s\n", path, strerror(errno));
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);
2440 * Now scan for the service scripts and their LSB comments.
2445 * Scan scripts found in the command line to be able to resolve
2446 * all dependcies given within those scripts.
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);
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);
2458 warn("warning: script '%s' missing LSB tags\n", name);
2461 if (!script_inf.provides || script_inf.provides == empty)
2462 script_inf.provides = xstrdup(name);
2464 provides = xstrdup(script_inf.provides);
2466 while ((token = strsep(&begin, delimeter)) && *token) {
2467 service_t * service;
2469 if (*token == '$') {
2470 warn("script %s provides system facility %s, skipped!\n", name, token);
2473 if (*token == '#') {
2474 warn("script %s provides facility %s with comment sign, skipped!\n", name, token);
2478 service = addservice(token);
2481 nickservice(first, service);
2485 service->attr.flags |= SERV_CMDLINE;
2491 * Scan now all scripts found in the init.d/ directory
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;
2497 char * begin = (char*)0; /* hold start pointer of strings handled by strsep() */
2498 boolean hard = false;
2500 #if defined(DEBUG) && (DEBUG > 0)
2504 if (*d->d_name == '.')
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);
2513 if (!S_ISREG(st_script.st_mode) || !(S_IXUSR & st_script.st_mode))
2515 if (S_ISDIR(st_script.st_mode))
2518 warn("script %s is not an executable regular file, skipped!\n", d->d_name);
2522 if (!strncmp(d->d_name, "README", strlen("README"))) {
2524 warn("script name %s is not valid, skipped!\n", d->d_name);
2528 if (!strncmp(d->d_name, "Makefile", strlen("Makefile"))) {
2530 warn("script name %s is not valid, skipped!\n", d->d_name);
2534 if (!strncmp(d->d_name, "core", strlen("core"))) {
2536 warn("script name %s is not valid, skipped!\n", d->d_name);
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))
2546 warn("script name %s is not valid, skipped!\n", d->d_name);
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 */
2557 warn("script name %s is not valid, skipped!\n", d->d_name);
2561 if (cfgfile_filter(d) == 0) {
2563 warn("script name %s is not valid, skipped!\n", d->d_name);
2567 /* left by emacs like editors */
2568 if (d->d_name[strlen(d->d_name)-1] == '~') {
2570 warn("script name %s is not valid, skipped!\n", d->d_name);
2574 if (strspn(d->d_name, "$.#%_+-\\*[]^:()~")) {
2576 warn("script name %s is not valid, skipped!\n", d->d_name);
2580 /* main scanner for LSB comment in current script */
2581 lsb = scan_script_defaults(dfd, d->d_name, override_path, (char**)0, false, ignore);
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);
2587 warn("warning: script '%s' missing LSB tags\n", d->d_name);
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);
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);
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");
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);
2630 #endif /* not SUSE */
2633 * Oops, no comment found, guess one
2635 if (!script_inf.provides || script_inf.provides == empty) {
2637 script_inf.provides = xstrdup(d->d_name);
2640 * Use guessed service to find it within the the runlevels
2641 * (by using the list from the first scan for script locations).
2643 if ((guess = findservice(script_inf.provides))) {
2645 * Try to guess required services out from current scheme.
2646 * Note, this means that all services are required.
2648 if (!script_inf.required_start || script_inf.required_start == empty) {
2650 list_for_each_prev(ptr, s_start) {
2651 service_t * tmp = getservice(ptr);
2653 if (!tmp->attr.sorder)
2655 if (tmp->attr.sorder >= guess->attr.sorder)
2657 if (tmp->start->lvl & guess->start->lvl) {
2658 script_inf.required_start = xstrdup(tmp->name);
2663 if (!script_inf.required_stop || script_inf.required_stop == empty) {
2665 list_for_each_prev(ptr, s_start) {
2666 service_t * tmp = getservice(ptr);
2668 if (!tmp->attr.korder)
2670 if (tmp->attr.korder <= guess->attr.korder)
2672 if (tmp->stopp->lvl & guess->stopp->lvl) {
2673 script_inf.required_stop = xstrdup(tmp->name);
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);
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);
2687 } else { /* !findservice(&guess, script_inf.provides) */
2691 * Find out which levels this service may have out from current scheme.
2692 * Note, this means that the first requiring service wins.
2694 list_for_each(ptr, s_start) {
2698 if (script_inf.default_start && script_inf.default_start != empty)
2700 cur = getservice(ptr);
2703 if (list_empty(&cur->sort.req) || !(cur->attr.flags & SERV_ENABLED))
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);
2713 list_for_each(ptr, s_start) {
2717 if (script_inf.default_stop && script_inf.default_stop != empty)
2719 cur = getservice(ptr);
2722 if (list_empty(&cur->sort.rev) || !(cur->attr.flags & SERV_ENABLED))
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);
2732 } /* !findservice(&guess, script_inf.provides) */
2736 * Use guessed service to find it within the the runlevels
2737 * (by using the list from the first scan for script locations).
2740 char * provides = xstrdup(script_inf.provides);
2741 service_t * first = (service_t*)0;
2744 while ((token = strsep(&begin, delimeter)) && *token) {
2746 if (*token == '$') {
2747 warn("script %s provides system facility %s, skipped!\n", d->d_name, token);
2750 if (*token == '#') {
2751 warn("script %s provides facility %s with comment sign, skipped!\n", d->d_name, token);
2755 service = addservice(token);
2758 nickservice(first, service);
2762 #if defined(DEBUG) && (DEBUG > 0)
2765 if (!makeprov(service, d->d_name)) {
2767 if (!del || (del && !isarg))
2768 warn("script %s: service %s already provided!\n", d->d_name, token);
2770 if (!del && !ignore && isarg)
2771 error("exiting now!\n");
2773 if (!del || (del && !ignore && !isarg))
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);
2786 boolean known = (service->attr.flags & SERV_KNOWN);
2787 service->attr.flags |= SERV_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 */
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 */
2804 if (script_inf.required_stop && script_inf.required_stop != empty) {
2805 rememberreq(service, REQ_MUST|REQ_KILL, script_inf.required_stop);
2807 if (script_inf.should_stop && script_inf.should_stop != empty) {
2808 rememberreq(service, REQ_SHLD|REQ_KILL, script_inf.should_stop);
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 */
2819 if (script_inf.stop_after && script_inf.stop_after != empty) {
2820 reversereq(service, REQ_SHLD|REQ_KILL, script_inf.stop_after);
2823 * Use information from symbolic link structure to
2824 * check if all services are around for this script.
2826 if (isarg && !ignore) {
2829 ok = chkdependencies(service);
2831 ok = chkrequired(service);
2833 error("exiting now!\n");
2836 if (script_inf.default_start && script_inf.default_start != empty) {
2837 ushort deflvls = str2lvl(script_inf.default_start);
2839 if (service->attr.flags & SERV_ENABLED) {
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.
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));
2853 * Currently not linked into service runlevel scheme, info
2854 * needed for enabling interactive services at first time.
2856 service->start->lvl = deflvls;
2858 } else if (script_inf.default_start == empty) {
2859 if (service->attr.flags & SERV_ENABLED) {
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.
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);
2872 } else if (!script_inf.default_start && (service->attr.flags & SERV_NOTLSB)) {
2875 * Could be a none LSB script, use info from current link scheme.
2876 * If not found use default.
2878 if (service->attr.flags & SERV_ENABLED)
2879 script_inf.default_start = lvl2str(service->start->lvl);
2881 script_inf.default_start = xstrdup(DEFAULT_START_LVL);
2886 * This because SuSE boot script concept uses a differential link scheme.
2887 * Therefore default_stop is ignored and overwriten by default_start.
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);
2893 script_inf.default_stop = empty;
2894 oneway(script_inf.default_stop);
2896 if (script_inf.default_stop && script_inf.default_stop != empty) {
2897 ushort deflvlk = str2lvl(script_inf.default_stop);
2900 * Compare all bits, which means `==' and not `&' and overwrite
2901 * the defaults of the current script.
2903 if (service->attr.flags & SERV_ENABLED) {
2905 * Currently linked into service runlevel scheme, check
2906 * if the defaults are overwriten.
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));
2915 * Currently not linked into service runlevel scheme, info
2916 * needed for enabling interactive services at first time.
2918 service->stopp->lvl = deflvlk;
2920 } else if (script_inf.default_stop == empty) {
2921 if (service->attr.flags & SERV_ENABLED) {
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.
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);
2934 } else if (!script_inf.default_stop && (service->attr.flags & SERV_NOTLSB)) {
2937 * Could be a none LSB script, use info from current link scheme.
2938 * If not found use default.
2940 if (service->attr.flags & SERV_ENABLED)
2941 script_inf.default_stop = lvl2str(service->stopp->lvl);
2943 script_inf.default_stop = xstrdup(DEFAULT_STOP_LVL);
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);
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;
2966 #endif /* not 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);
2973 script_inf.default_stop = xstrdup(DEFAULT_STOP_LVL);
2974 oneway(script_inf.default_stop);
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;
2981 #endif /* not SUSE */
2983 if (isarg && !defaults && !del) {
2984 if (argr[curr_argc]) {
2985 char * ptr = argr[curr_argc];
2991 {"start=", (char*)0, &script_inf.default_start},
2992 {"stop=", (char*)0, &script_inf.default_stop },
2994 {"reqstart=", (char*)0, &script_inf.required_start},
2995 {"reqstop=", (char*)0, &script_inf.required_stop },
2997 {(char*)0, (char*)0, (char**)0}
3000 for (c = 0; mark[c].wrd; c++) {
3001 char * order = strstr(ptr, mark[c].wrd);
3003 mark[c].order = order;
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);
3012 for (c = 0; mark[c].wrd; c++)
3013 if (mark[c].order) {
3014 size_t len = strlen(mark[c].order);
3016 char * ptr = mark[c].order + len - 1;
3017 if (*ptr == ',') *ptr = '\0';
3019 xreset(*(mark[c].str));
3020 *(mark[c].str) = xstrdup(mark[c].order);
3025 * This because SuSE boot script concept uses a differential link scheme.
3026 * Therefore default_stop is ignored and overwriten by default_start.
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);
3037 #if defined(DEBUG) && (DEBUG > 0)
3039 fprintf(stderr, "internal BUG at line %d with script %s\n", __LINE__, d->d_name);
3044 begin = script_inf.provides;
3045 while ((token = strsep(&script_inf.provides, delimeter)) && *token) {
3051 service = addservice(token);
3052 service = getorig(service);
3054 if ((service->attr.flags & SERV_ENABLED) && !hard) {
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);
3066 script_inf.provides = begin;
3068 /* Remember if not LSB conform script */
3069 if (!lsb && service) {
3070 service = getorig(service);
3071 service->attr.flags |= SERV_NOTLSB;
3074 /* Reset remaining pointers */
3075 scan_script_reset();
3078 * Free the regular scanner for the scripts.
3080 scan_script_regfree();
3087 * Clear out aliases found for all scripts.
3092 * Set virtual dependencies for already enabled none LSB scripts.
3097 * Now generate for all scripts the dependencies
3100 if (is_loop_detected() && !ignore)
3101 error("exiting without changing boot order!\n");
3104 * Be sure that interactive scripts are the only member of
3105 * a start group (for parallel start only).
3110 * Move the `$all' scripts to the end of all
3115 * Sorry but we support only [KS][0-9][0-9]<name>
3117 if (maxstart > MAX_DEEP || maxstop > MAX_DEEP)
3118 error("Maximum of %u in ordering reached\n", MAX_DEEP);
3120 #if defined(DEBUG) && (DEBUG > 0)
3121 printf("Maxorder %d/%d\n", maxstart, maxstop);
3124 # ifdef SUSE /* SuSE's SystemV link scheme */
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;
3134 if ((rcd = map_runlevel_to_location(runlevel)) == (char*)0)
3137 rcdir = openrcdir(rcd); /* Creates runlevel directory if necessary */
3138 if (rcdir == (DIR*)0)
3140 if ((dfd = dirfd(rcdir)) < 0) {
3147 * See if we found scripts which should not be
3148 * included within this runlevel directory.
3150 while ((d = readdir(rcdir)) != (struct dirent*)0) {
3151 const char * ptr = d->d_name;
3154 if (*ptr != 'S' && *ptr != 'K')
3159 if (strspn(ptr, "0123456789") != 2)
3163 if (xstat(dfd, d->d_name, &st_script) < 0)
3164 xremove(dfd, d->d_name); /* dangling sym link */
3166 if (notincluded(ptr, type, runlevel)) {
3167 serv = findservice(getprovides(ptr));
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;
3187 * Seek for scripts which are included, link or
3188 * correct order number if necessary.
3192 while ((serv = listscripts(&script, 'X', lvl))) {
3193 const boolean this = chkfor(script, argv, argc);
3194 boolean found, slink;
3197 if (*script == '$') /* Do not link in virtual dependencies */
3201 if ((serv->start->lvl & lvl) == 0)
3204 sprintf(olink, "../%s", script);
3205 sprintf(nlink, "S%.2d%s", serv->attr.sorder, script);
3209 while ((clink = scan_for(rcdir, script, 'S'))) {
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;
3216 xsymlink(dfd, olink, nlink); /* Not ours, but correct order */
3217 if (++serv->attr.ref)
3218 serv->attr.flags |= SERV_ENABLED;
3221 xsymlink(dfd, olink, nlink); /* Restore, with correct order */
3222 if (++serv->attr.ref)
3223 serv->attr.flags |= SERV_ENABLED;
3227 xremove(dfd, clink); /* Found it, remove link */
3228 if (--serv->attr.ref <= 0)
3229 serv->attr.flags &= ~SERV_ENABLED;
3236 * If we haven't found it and we shouldn't delete it
3239 if (!del && !found) {
3240 xsymlink(dfd, olink, nlink);
3241 if (++serv->attr.ref)
3242 serv->attr.flags |= SERV_ENABLED;
3247 /* Start links done, now do Kill links */
3251 if ((serv->stopp->lvl & lvl) == 0)
3254 sprintf(olink, "../%s", script);
3255 sprintf(nlink, "K%.2d%s", serv->attr.korder, script);
3259 while ((clink = scan_for(rcdir, script, 'K'))) {
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;
3266 xsymlink(dfd, olink, nlink); /* Not ours, but correct order */
3267 if (++serv->attr.ref)
3268 serv->attr.flags |= SERV_ENABLED;
3271 xsymlink(dfd, olink, nlink); /* Restore, with correct order */
3272 if (++serv->attr.ref)
3273 serv->attr.flags |= SERV_ENABLED;
3277 xremove(dfd, clink); /* Found it, remove link */
3278 if (--serv->attr.ref <= 0)
3279 serv->attr.flags &= ~SERV_ENABLED;
3284 if (this && slink) {
3286 * If we haven't found it and we shouldn't delete it
3289 if (!del && !found) {
3290 xsymlink(dfd, olink, nlink);
3291 if (++serv->attr.ref)
3292 serv->attr.flags |= SERV_ENABLED;
3299 # else /* not SUSE but Debian SystemV link scheme */
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.
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;
3317 if ((rcd = map_runlevel_to_location(runlevel)) == (char*)0)
3319 lvl = map_runlevel_to_lvl(runlevel);
3320 seek = map_runlevel_to_seek(runlevel);
3322 rcdir = openrcdir(rcd); /* Creates runlevel directory if necessary */
3323 if (rcdir == (DIR*)0)
3325 if ((dfd = dirfd(rcdir)) < 0) {
3332 * See if we found scripts which should not be
3333 * included within this runlevel directory.
3335 while ((d = readdir(rcdir)) != (struct dirent*)0) {
3336 const char * ptr = d->d_name;
3339 if (*ptr != 'S' && *ptr != 'K')
3344 if (strspn(ptr, "0123456789") != 2)
3348 if (xstat(dfd, d->d_name, &st_script) < 0)
3349 xremove(dfd, d->d_name); /* dangling sym link */
3351 if (notincluded(ptr, type, runlevel)) {
3352 serv = findservice(getprovides(ptr));
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;
3373 while ((serv = listscripts(&script, 'X', seek))) {
3374 const boolean this = chkfor(script, argv, argc);
3379 if (*script == '$') /* Do not link in virtual dependencies */
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 */
3387 # endif /* USE_KILL_IN_BOOT */
3388 sprintf(nlink, "K%.2d%s", serv->attr.korder, script);
3390 } else if (serv->start->lvl & lvl) {
3391 sprintf(nlink, "S%.2d%s", serv->attr.sorder, script);
3394 continue; /* We aren't suppose to be on this runlevel */
3399 while ((clink = scan_for(rcdir, script, mode))) {
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;
3406 xsymlink(dfd, olink, nlink); /* Not ours, but correct order */
3407 if (++serv->attr.ref)
3408 serv->attr.flags |= SERV_ENABLED;
3411 xsymlink(dfd, olink, nlink); /* Restore, with correct order */
3412 if (++serv->attr.ref)
3413 serv->attr.flags |= SERV_ENABLED;
3417 xremove(dfd, clink); /* Found it, remove link */
3418 if (--serv->attr.ref <= 0)
3419 serv->attr.flags &= ~SERV_ENABLED;
3426 * If we haven't found it and we shouldn't delete it
3429 if (!del && !found) {
3430 xsymlink(dfd, olink, nlink);
3431 if (++serv->attr.ref)
3432 serv->attr.flags |= SERV_ENABLED;
3441 # endif /* !SUSE, standard SystemV link scheme */
3450 * Back to the root(s)
3455 * Make valgrind happy
3457 if (path != ipath) free(path);
3458 if (root) free(root);