insserv-1.14.0/0000755017777601777750000000000011366260206012711 5ustar nobodynobodyinsserv-1.14.0/listing.h0000644017777601777750000003177711263111260014541 0ustar nobodynobody/* * listing.h * * Copyright 2000,2009 Werner Fink, 2000 SuSE GmbH Nuernberg, Germany. * 2008,2009 SuSE Linux Products GmbH Nuernberg, Germany * * This source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include "config.h" typedef enum _boolean {false, true} boolean; typedef unsigned char uchar; #ifndef __USE_MISC typedef unsigned short ushort; typedef unsigned int uint; #endif #ifndef __OPTIMIZE__ # warning This will not compile without -O at least #endif #if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) # ifndef inline # define inline __inline__ # endif # ifndef restrict # define restrict __restrict__ # endif # ifndef volatile # define volatile __volatile__ # endif # ifndef asm # define asm __asm__ # endif # ifndef extension # define extension __extension__ # endif #endif #ifndef attribute # define attribute(attr) __attribute__(attr) #endif /* * This is lent from the kernel by e.g. using * * echo '#include \nint main () { prefetch(); return 0; }' | \ * gcc -I/usr/src/linux/include -D__KERNEL__ -x c -E -P - | \ * sed -rn '/void[[:blank:]]+prefetch[[:blank:]]*\(/,/^}/p' * * on the appropiate architecture (here on i686 for i586). */ static inline void prefetch(const void *restrict x) attribute((used,always_inline)); static inline void prefetch(const void *restrict x) { #if defined(__x86_64__) asm volatile ("prefetcht0 %0" :: "m" (*(unsigned long *)x)) #elif defined(__ia64__) asm volatile ("lfetch [%0]" :: "r" (x)) #elif defined(__powerpc64__) asm volatile ("dcbt 0,%0" :: "r" (x)) #elif 1 && defined(__i386__) asm volatile ("661:\n\t" ".byte 0x8d,0x74,0x26,0x00\n" "\n662:\n" ".section .altinstructions,\"a\"\n" " .align 4\n" " .long 661b\n" " .long 663f\n" " .byte %c0\n" " .byte 662b-661b\n" " .byte 664f-663f\n" ".previous\n" ".section .altinstr_replacement,\"ax\"\n" " 663:\n\t" " prefetchnta (%1)" " \n664:\n" ".previous" :: "i" ((0*32+25)), "r" (x)) #endif ; } #if defined(DEBUG) && (DEBUG > 0) # define __align attribute((packed)) #else # define __align attribute((aligned(sizeof(struct list_struct*)))) #endif #define __packed attribute((packed)) #define alignof(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1)) #define strsize(string) ((strlen(string)+1)*sizeof(char)) typedef struct list_struct { struct list_struct * next, * prev; } __align list_t; /* * Linked list handling * ==================== * The structures which will be linked into such lists have to be of the * same type. The structures may have alway a list identifier of the type * `list_t' as very first element. With this the macro list_entry() can * be used to cast the memory address of a list member to the corresponding * allocated structure. */ /* * Insert new entry as next member. */ static inline void insert(list_t *restrict new, list_t *restrict here) attribute((always_inline,nonnull(1,2))); static inline void insert(list_t *restrict new, list_t *restrict here) { list_t * prev = here; list_t * next = here->next; next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /* * Set head */ static inline void initial(list_t *restrict head) attribute((always_inline,nonnull(1))); static inline void initial(list_t *restrict head) { head->prev = head->next = head; } /* * Remove entries, note that the pointer its self remains. */ static inline void delete(list_t *restrict entry) attribute((always_inline,nonnull(1))); static inline void delete(list_t *restrict entry) { list_t * prev = entry->prev; list_t * next = entry->next; next->prev = prev; prev->next = next; initial(entry); } static inline void join(list_t *restrict list, list_t *restrict head) attribute((always_inline,nonnull(1,2))); static inline void join(list_t *restrict list, list_t *restrict head) { list_t * first = list->next; if (first != list) { list_t * last = list->prev; list_t * at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } } static inline boolean list_empty(list_t *restrict head) attribute((always_inline,nonnull(1))); static inline boolean list_empty(list_t *restrict head) { return head->next == head; } static inline void move_tail(list_t *restrict entry, list_t *restrict head) attribute((always_inline,nonnull(1,2))); static inline void move_tail(list_t *restrict entry, list_t *restrict head) { list_t * prev = entry->prev; list_t * next = entry->next; next->prev = prev; /* remove enty from old list */ prev->next = next; prev = head->prev; next = head; next->prev = entry; /* and add it at tail of new list */ entry->next = next; entry->prev = prev; prev->next = entry; } #define list_entry(ptr, type, member) (__extension__ ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ((type *)( (char *)(__mptr) - offsetof(type,member) )); })) #define list_for_each(pos, head) \ for (pos = (head)->next; prefetch(pos->next), pos != (head); pos = pos->next) #define np_list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define list_for_each_safe(pos, safe, head) \ for (pos = (head)->next, safe = pos->next; pos != (head); pos = safe, safe = pos->next) #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; prefetch(pos->prev), pos != (head); pos = pos->prev) #define np_list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /* * The runlevel bits within own struct */ typedef struct level_struct { ushort lvl; } __packed level_t; /* * Common attributes */ typedef struct attr_struct { ushort flags; short ref; uchar sorder; uchar korder; char *script; } __packed attr_t; /* * Linked list of required services (start/stop) */ typedef struct sort_struct { list_t req, rev; } __align sort_t; /* * Objects of linked list of required services */ typedef struct service_struct service_t; typedef struct req_serv { list_t list; ushort flags; service_t *restrict serv; } __align req_t; #define getreq(arg) list_entry((arg), struct req_serv, list) /* * Used by findservice() */ struct service_struct { list_t s_list; sort_t sort; void *restrict dir; level_t *restrict start; level_t *restrict stopp; attr_t attr; char * name; } __align; #define getservice(list) list_entry((list), service_t, s_list) extern list_t * s_start; extern int maxstart; extern int maxstop; extern void clear_all(void); extern void nickservice(service_t *restrict orig, service_t *restrict nick) attribute((nonnull(1,2))); extern void follow_all(void); extern void show_all(void); extern void requires(service_t *restrict this, service_t *restrict dep, const char mode) attribute((nonnull(1,2))); extern void runlevels(service_t *restrict serv, const char mode, const char *restrict lvl) attribute((nonnull(1,3))); extern boolean makeprov(service_t *restrict serv, const char *restrict script) attribute((nonnull(1,2))); extern void setorder(const char *restrict script, const char mode, const int order, const boolean recursive) attribute((nonnull(1))); extern int getorder(const char *restrict script, const char mode) attribute((nonnull(1))); extern boolean notincluded(const char *restrict const script, const char mode, const int runlevel) attribute((nonnull(1))); extern const char * getscript(const char *restrict prov) attribute((nonnull(1))); extern const char * getprovides(const char *restrict script) attribute((nonnull(1))); extern service_t * listscripts(const char **restrict script, const char mode, const ushort lvl); extern boolean is_loop_detected(void); extern service_t * addservice(const char *restrict const serv) attribute((malloc,nonnull(1))); extern service_t * findservice(const char *restrict const name); extern service_t * getorig(service_t *restrict serv) attribute((const,nonnull(1))); extern void lsort(const char type); /* * Common short cuts */ extern const char *const delimeter; extern void error(const char *restrict fmt, ...) attribute((noreturn,format(printf,1,2))); extern void warn (const char *restrict fmt, ...) attribute((format(printf,1,2))); extern void info (int level, const char *restrict fmt, ...) attribute((format(printf,2,3))); extern inline int map_has_runlevels(void) attribute((always_inline)); extern inline char map_runlevel_to_key(const int runlevel); extern inline ushort map_key_to_lvl(const char key); extern inline const char *map_runlevel_to_location(const int runlevel); extern inline ushort map_runlevel_to_lvl(const int runlevel); extern inline ushort map_runlevel_to_seek(const int runlevel); extern ushort str2lvl(const char *restrict lvl) attribute((nonnull(1))); extern char * lvl2str(const ushort lvl); static inline char * xstrdup(const char *restrict s) attribute((always_inline,malloc)); static inline char * xstrdup(const char *restrict s) { char * r; if (!s) error("%s", strerror(EINVAL)); if (!(r = strdup(s))) error("%s", strerror(errno)); return r; } #define xreset(ptr) \ {char *restrict tmp = (char *restrict)ptr; if (ptr && *tmp) free(ptr);} ptr = NULL #if defined(HAS_unlinkat) && defined(_ATFILE_SOURCE) && !defined(__stub_unlinkat) # define xremove(d,x) (__extension__ ({ if ((dryrun ? 0 : \ (unlinkat(d,x,0) != 0 && (errno != EISDIR || unlinkat(d,x,AT_REMOVEDIR) != 0)))) \ warn ("can not remove(%s%s): %s\n", rcd, x, strerror(errno)); \ else \ info(1, "remove service %s/%s%s\n", path, rcd, x); })) #else # define xremove(d,x) (__extension__ ({ if ((dryrun ? 0 : (remove(x) != 0))) \ warn ("can not remove(%s%s): %s\n", rcd, x, strerror(errno)); \ else \ info(1, "remove service %s/%s%s\n", path, rcd, x); })) #endif #if defined(HAS_symlinkat) && defined(_ATFILE_SOURCE) && !defined(__stub_symlinkat) # define xsymlink(d,x,y) (__extension__ ({ if ((dryrun ? 0 : (symlinkat(x, d, y) != 0))) \ warn ("can not symlink(%s, %s%s): %s\n", x, rcd, y, strerror(errno)); \ else \ info(1, "enable service %s -> %s/%s%s\n", x, path, rcd, y); })) #else # define xsymlink(d,x,y) (__extension__ ({ if ((dryrun ? 0 : (symlink(x, y) != 0))) \ warn ("can not symlink(%s, %s%s): %s\n", x, rcd, y, strerror(errno)); \ else \ info(1, "enable service %s -> %s/%s%s\n", x, path, rcd, y); })) #endif #if defined(HAS_fstatat) && defined(_ATFILE_SOURCE) && !defined(__stub_fstatat) # define xstat(d,x,s) (__extension__ ({ fstatat(d,x,s, 0); })) # define xlstat(d,x,s) (__extension__ ({ fstatat(d,x,s, AT_SYMLINK_NOFOLLOW); })) #else # define xstat(d,x,s) (__extension__ ({ stat(x,s); })) # define xlstat(d,x,s) (__extension__ ({ lstat(x,s); })) #endif #if defined(HAS_readlinkat) && defined(_ATFILE_SOURCE) && !defined(__stub_readlinkat) # define xreadlink(d,x,b,l) (__extension__ ({ readlinkat(d,x,b,l); })) #else # define xreadlink(d,x,b,l) (__extension__ ({ readlink(x,b,l); })) #endif #if defined(HAS_openat) && defined(_ATFILE_SOURCE) && !defined(__stub_openat) # define xopen(d,x,f) (__extension__ ({ openat(d,x,f); })) #else # define xopen(d,x,f) (__extension__ ({ open(x,f); })) #endif /* * Bits of the requests */ #define REQ_MUST 0x0001 #define REQ_SHLD 0x0002 #define REQ_KILL 0x0004 /* * Bits of the services */ #define SERV_KNOWN 0x0001 #define SERV_NOTLSB 0x0002 #define SERV_ALREADY 0x0004 #define SERV_INTRACT 0x0008 #define SERV_ENABLED 0x0010 #define SERV_ALL 0x0020 #define SERV_DUPLET 0x0040 #define SERV_SCRIPT 0x0080 #define SERV_NOSTOP 0x0100 #define SERV_CMDLINE 0x0200 #define SERV_FIRST 0x0400 /* * Bits of the runlevels */ #define LVL_HALT 0x0001 #define LVL_ONE 0x0002 #define LVL_TWO 0x0004 #define LVL_THREE 0x0008 #define LVL_FOUR 0x0010 #define LVL_FIVE 0x0020 #define LVL_REBOOT 0x0040 #ifdef SUSE # define LVL_SINGLE 0x0080 # define LVL_BOOT 0x0100 #else # define LVL_SINGLE 0x0000 # define LVL_BOOT 0x0080 #endif /* * LVL_BOOT is already done if one of the LVL_ALL will be entered. */ #define LVL_ALL (LVL_HALT|LVL_ONE|LVL_TWO|LVL_THREE|LVL_FOUR|LVL_FIVE|LVL_REBOOT|LVL_SINGLE) /* * Normal runlevels which are _direct_ available by shutdown/reboot/halt */ #define LVL_NORM (LVL_HALT|LVL_ONE|LVL_TWO|LVL_THREE|LVL_FOUR|LVL_FIVE|LVL_REBOOT) /* * Oneway runlevels at shutdown/reboot/halt/single */ #define LVL_ONEWAY (LVL_HALT|LVL_ONE|LVL_REBOOT|LVL_SINGLE) /* * Maximum start/stop level */ #define MAX_DEEP 99 insserv-1.14.0/tests/0000755017777601777750000000000011366260206014053 5ustar nobodynobodyinsserv-1.14.0/tests/common0000755017777601777750000000455311337521072015277 0ustar nobodynobody#!/bin/bash basedir=$(dirname $0) . $basedir/suite ########################################################################## test_simple_sequence() { echo echo "info: test simple script ordering." echo initdir_purge insertscript firstscript <<'EOF' || true ### BEGIN INIT INFO # Provides: firstscript # Required-Start: # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: ### END INIT INFO EOF insertscript middlescript <<'EOF' || true ### BEGIN INIT INFO # Provides: middlescript # Required-Start: firstscript # Required-Stop: firstscript # Default-Start: 2 3 4 5 # Default-Stop: ### END INIT INFO EOF insertscript lastscript <<'EOF' || true ### BEGIN INIT INFO # Provides: lastscript # Required-Start: middlescript # Required-Stop: middlescript # Default-Start: 2 3 4 5 # Default-Stop: ### END INIT INFO EOF list_rclinks check_script_present 2 firstscript check_script_present 3 firstscript check_script_present 4 firstscript check_script_present 5 firstscript check_order 3 firstscript middlescript check_order 3 middlescript lastscript } ########################################################################## test_undetected_loop() { echo echo "info: test if loop involving virtual facility is detected." echo initdir_purge set +C cat <<'EOF' > $insconf $local_fs mountall EOF set -C insertscript hibernate < /dev/null echo ${initddir##*/}: ls ${1+"$@"} * popd &> /dev/null } cat <<-'EOF' > $insconf $local_fs boot.localfs +boot.crypto $network network $named +named +dnsmasq +lwresd $network $remote_fs $local_fs +nfs +smbfs $syslog syslog $portmap portmap $time boot.clock +ntp boot.crypto boot.clock boot.localfs boot.rootfsck apache apache2 kdump ntp EOF else runlevel_path () { printf "${initddir}/../rc%s.d\n" ${1+"$@"} } list_rclinks() { pushd $initddir/../ &> /dev/null ls ${1+"$@"} * popd &> /dev/null } cat <<-'EOF' > $insconf $local_fs +mountall +umountfs $network +networking +ifupdown $named +named +dnsmasq +lwresd +bind9 $network $remote_fs $local_fs +mountnfs +mountnfs-bootclean +umountnfs +sendsigs $syslog +syslog +sysklogd $portmap portmap $time hwclock udev mountdevsubfs checkroot checkfs console-screen EOF update_conf () { set +C cat <<-'EOF' > $insconf $local_fs +mountall +umountfs $network +networking +ifupdown $named +named +dnsmasq +lwresd +bind9 $network $remote_fs $local_fs +mountnfs +mountnfs-bootclean +umountnfs +sendsigs $syslog +syslog +sysklogd $portmap portmap $time hwclock udev mountdevsubfs checkroot checkfs console-screen EOF set -C } fi chmod u+w,a+r $insconf insserv_reg () { script=$(printf "${initddir}/%s\n" ${1+"$@"}) $insserv $debug -c $insconf -p $initddir -o $overridedir $script } insserv_del () { script=$(printf "${initddir}/%s\n" ${1+"$@"}) $insserv $debug -c $insconf -p $initddir -o $overridedir -r $script } initdir_purge () { rm -rf ${initddir}/../rc*.d ${initddir} mkdir -p ${initddir} } relpath () { local OLDIFS IFS local -a orgwords local -a locwords local -i relp=0 local -i deep=0 local p l local path="" OLDIFS="$IFS"; IFS=/ eval orgwords=(${1// /\\\\ }) ; shift eval locwords=(${1// /\\\\ }) ; shift IFS="$OLDIFS" unset OLDIFS deep=0 relp=0 for l in "${locwords[@]}" ; do if test "$l" = ".." ; then ((deep++)) continue elif test $deep -gt 0 ; then while ((deep-- > 0)) ; do unset locwords[$((relp+deep))] (((relp-1)-deep < 0)) || unset locwords[$(((relp-1)-deep))] done continue fi ((relp++)) done locwords=(${locwords[@]}) deep=0 relp=0 for p in "${orgwords[@]}" ; do if test "$p" = ".." ; then ((deep++)) continue elif test $deep -gt 0 ; then while ((deep-- > 0)) ; do unset orgwords[$((relp+deep))] (((relp-1)-deep < 0)) || unset orgwords[$(((relp-1)-deep))] done continue fi ((relp++)) done orgwords=(${orgwords[@]}) deep=0 relp=0 for p in "${orgwords[@]}" ; do eval l="\${locwords[$((deep++))]}" if test "$l" != "$p" -o $relp -ne 0 ; then ((relp++)) path="${path}/$p" test -n "$l" || continue if test $relp -eq 1 ; then path="..${path}" else path="../${path}" fi fi done unset orgwords p l if test $deep -lt ${#locwords[@]} ; then relp=0 while test $relp -lt $deep; do unset locwords[$((relp++))] done while test ${#locwords[@]} -gt 0 ; do path="../${path}" unset locwords[$((relp++))] done fi echo "$path" } counttest () { testcount=$(expr $testcount + 1) } error () { echo error: $@ checkfailed=$(expr $checkfailed + 1) retval=1 } warning () { echo warning: $@ testfailed=$(expr $testfailed + 1) } addscript () { local scriptname=$1 local script=${initddir}/$scriptname cat > $script chmod u+w,a+rx $script } remscript () { local scriptname=$1 local script=${initddir}/$scriptname rm -f $script } insertscript () { local scriptname=$1 addscript $scriptname insserv_reg $scriptname } present_ok () { local rcdpath=$(runlevel_path $1); shift local script=$1; shift local ret=1 for f in ${rcdpath}/[KS][0-9][0-9]$script ; do if [ -L $f ] ; then ret=0 fi done counttest return $ret } check_script_present () { local runlevel=$1; shift local script=$1; shift present_ok $runlevel $script && return 0 error "script $script not present in runlevel $runlevel" } test_script_present () { local runlevel=$1; shift local script=$1; shift present_ok $runlevel $script && return 0 warning "script $script not present in runlevel $runlevel" } check_script_not_present () { local runlevel=$1; shift local script=$1; shift if present_ok $runlevel $script ; then error "script $script present in runlevel $runlevel" fi return 0 } test_script_not_present () { local runlevel=$1; shift local script=$1; shift if present_ok $runlevel $script ; then warning "script $script present in runlevel $runlevel" fi return 0 } order_ok () { local rcdpath=$(runlevel_path $1); shift local script1=$1; shift local script2=$1; shift local ret=0 scr order="" pushd $rcdpath &> /dev/null if test -n "${issuse}" ; then for scr in S[0-9][0-9]* ; do test -e "${scr}" || continue test -L "${scr}" || continue case "${scr#S[0-9][0-9]}" in ${script1}) order="${order:+$order }${script1}" ;; ${script2}) order="${order:+$order }${script2}" ;; esac done else for scr in [SK][0-9][0-9]* ; do test -e "${scr}" || continue test -L "${scr}" || continue case "${scr#[SK][0-9][0-9]}" in ${script1}) order="${order:+$order }${script1}" ;; ${script2}) order="${order:+$order }${script2}" ;; esac done fi popd &> /dev/null test "$order" = "$script1 $script2" || ret=1 counttest return $ret } # Fatal check check_order () { local runlevel=$1 local script1=$2 local script2=$3 order_ok ${1+"$@"} || error "incorrect $runlevel sequence $script1 not before $script2" || true } # Non-fatal check test_order () { local runlevel=$1 local script1=$2 local script2=$3 order_ok ${1+"$@"} || warning "incorrect $runlevel sequence $script1 not before $script2" || true } insserv-1.14.0/insserv.8.in0000644017777601777750000003075011337506464015114 0ustar nobodynobody.\" .\" Copyright 2000-2009 Werner Fink .\" Copyright 2000-2003 SuSE GmbH Nuernberg, Germany .\" Copyright 2007-2009 SuSE Linux Products GmbH Nuernberg, Germany .\" Copyright 2009 Petter Reinholdtsen .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by .\" the Free Software Foundation; either version 2 of the License, or .\" (at your option) any later version. .\" .\" This program is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA .\" @@BEGIN_SUSE@@ .TH INSSERV 8 "Jul 29, 2008" "Version 1.11" "The SuSE boot concept" .UC 8 @@ELSE_SUSE@@ .TH INSSERV 8 "Jul 29, 2008" "Version 1.11" .UC 8 @@END_SUSE@@ .SH NAME insserv \- Enable an installed system init script .SH SYNOPSIS .\" .B insserv .RB [ \-v ] .RB [ \-c\ ] .RB [ \-p\ ] .RB [ \-d ] .RB [ \-f ] .RI [[ / ] path/to/init.d/ ] script \ ... .PP .B insserv .RB [ \-v ] .RB [ \-c\ ] .RB [ \-p\ ] .RI [[ / ] path/to/init.d/ ] script [ ,start=,stop= ] .PP .B insserv .RB [ \-v ] .RB [ \-c\ ] .RB [ \-p\ ] .B \-r .RB [ \-d ] .RB [ \-f ] .RI [[ / ] path/to/init.d/ ] script \ ... .PP .B insserv .B \-h .PP @@BEGIN_SUSE@@ .RB /usr/lib/lsb/install_initd .RI [[ / ] path/to/init.d/script ] .PP .RB /usr/lib/lsb/remove_initd .RI [[ / ] path/to/init.d/script ] @@END_SUSE@@ .SH DESCRIPTION .B insserv enables an installed system init script (`boot script') by reading the comment header of the script, e.g.: .sp 1 .in +1l .nf ### BEGIN INIT INFO # Provides: boot_facility_1 [ boot_facility_2 ...] # Required-Start: boot_facility_1 [ boot_facility_2 ...] # Required-Stop: boot_facility_1 [ boot_facility_2 ...] # Should-Start: boot_facility_1 [ boot_facility_2 ...] # Should-Stop: boot_facility_1 [ boot_facility_2 ...] # X-Start-Before: boot_facility_1 [ boot_facility_2 ...] # X-Stop-After: boot_facility_1 [ boot_facility_2 ...] # Default-Start: run_level_1 [ run_level_2 ...] # Default-Stop: run_level_1 [ run_level_2 ...] # X-Interactive: true # Short-Description: single_line_description # Description: multiline_description ### END INIT INFO .fi .in -1l .sp 1 and calculating the dependencies between all scripts. @@BEGIN_SUSE@@ Please note, that the .B Default\-Stop are ignored in SuSE Linux, because the SuSE boot script concept uses a differential link scheme (see .IR init.d (7)). @@ELSE_SUSE@@ Please be aware that the line .sp 1 .in +1l .nf # Required-Stop: boot_facility_1 [ boot_facility_2 ...] .fi .in -1l .sp 1 declares facilities which must be available during shutdown of the service declared in the .B Provides tag. Same holds true for .sp 1 .in +1l .nf # Should-Stop: boot_facility_1 [ boot_facility_2 ...] .fi .in -1l .sp 1 which declares facilities which should be available during shutdown of the service declared in the .B Provides tag. In both cases the script system should avoid stopping services which are declared by these two Stop tags until the script including these tags is stopped. @@END_SUSE@@ .PP The optional X\-Interactive keyword implies that the script using this keyword should be started alone in a concurrent boot configuration because it interact with the user at the console. Only the value `true' is recogniced. All other are ignored. .PP The optional .B X\-Start\-Before keyword implies that the script using this keyword should be started .B before the specified service names. Whereas the optional .B X\-Stop\-After keyword implies that the script using this keyword should be stopped .B after the specified service names. Both implies that those services now depend on the specifying script. With known dependencies and runlevel(s) .B insserv sets and reorders the corresponding symbolic links of the concerned runlevels @@BEGIN_SUSE@@ directories (see \fI init.d\fR (7)). @@ELSE_SUSE@@ directories. @@END_SUSE@@ @@BEGIN_SUSE@@ Known runlevels are: .sp 1 .in +1l .nf \fB0\fR\ used for System halt \fB1\fR\ used for single user mode \fB2\fR\ used for local multiuser without remote network \fB3\fR\ used for full multiuser with network \fB4\fR\ reserved for local use \fB5\fR\ used for full multiuser with network and xdm \fB6\fR\ used for System reboot \fBS\fR\ used during boot into single user mode \fBB\fR\ used during boot before any other runlevel .fi .in -1l .sp 1 @@END_SUSE@@ .PP .B insserv scans for .B System Facilities in the configuration file .I /etc/insserv.conf and each file in the directory .IR /etc/insserv.conf.d/ . Each line which begins with .B $ and a following name defines a system facility accordingly to the Linux Standard Base Specification (LSB), All names followed by such a system facility will declare the required dependencies of the facility. Here is an example for .IR /etc/insserv.conf : .sp 1 .in +1l .nf # All local filesystems are mounted # (done during boot phase) $local_fs boot # Low level networking $network network route # Named is operational $named named # All remote filesystems are mounted # (in some cases /usr may be remote). $remote_fs $local_fs nfs # System logger is operational $syslog syslog # All network daemons are running (This was removed in LSB 1.2) $netdaemons portmap inetd # Services which need to be interactive boot.crypto .fi .in -1l .sp 1 Names starting with a `+' sign are marked as optional. If the service with the name after the plus sign is available it will be used, if not available it is ignored silently. Words beginning with .B < and ending with .B > are keywords. Currently .B is the only know keyword for marking a service as an interactive one, e.g. a service which requires a passphrase or password input during boot or runlevel change. The special facility .B $null is used to enforce an empty dependency in case of .B Should-Stop and @@BEGIN_SUSE@@ .B Required-Stop otherwise insserv assumes the same dependencies as for the .B Start case. @@ELSE_SUSE@@ .BR Required-Stop . @@END_SUSE@@ .P In addition to the defined .B System Facilities in the configuration file .IR /etc/insserv.conf , .B insserv also knows the special facility .BR $all . This facility indicates that a service should be inserted at the end of all services at starting and at the very begining at stopping. Clearly all services using this facility will be grouped into one starting or stopping order. .\" .SH OPTIONS Currently there exists nine options for .BR insserv . .TP .BR \-v ,\ \-\-verbose Write out what is currently going on. .TP .BR \-c\ ,\ \-\-config\ Specify path to the insserv.conf file and the insserv.conf.d directory. Useful for testing. .TP .BR \-o\ ,\ \-\-override\ LSB comment headers found in this path will override existing LSB comment headers of scripts in the init.d directory (default path is .IR /etc/insserv/overrides/ ). .TP .BR \-p\ ,\ \-\-path\ Specify path to init.d directory. Useful for testing. .TP .BR \-n ,\ \-\-dryrun Do not update symlinks. .TP .BR \-r ,\ \-\-remove Remove the listed scripts from all runlevels. .TP .BR \-d ,\ \-\-default Use default runlevels as defined in the scripts. This may restore an edited runlevel link scheme. .TP .BR \-f ,\ \-\-force Ignore if a required service is missed. Beside this if start and or stop levels are specified on the command line the default levels of the script will be ignored. .TP .BR \-u\ ,\ \-\-upstart-job\ Path to replace existing upstart job path. (default path is .IR /lib/init/upstart-job ). .TP .BR \-h ,\ \-\-help Print out short usage message. .PP But you may use the argument syntax described in the following section. .SH ARGUMENTS .TP .RI [[ / ] path/to/init.d/ ] Relative or absolute path to the init scripts base directory. @@BEGIN_SUSE@@ For the SuSE Linux boot concept, this defaults to @@ELSE_SUSE@@ This defaults to @@END_SUSE@@ .I /etc/init.d/ in compliance with the LSB specification. In this case .B insserv does not add or remove a script to the runlevels declared in the script headers, but may re\-order the runlevels if the order of the currently enabled scripts has changed (see option .BR \-d ). Note that if a relative path is used .B insserv has to be called from the root directory. .TP .RI [[ / ] path/to/init.d/ ] script\ ... List of scripts which have to be added to the runlevels. If a path is used it should point to the absolute or relative location of the boot scripts. .B insserv checks for the existence of these scripts. For the runlevels the information found in the script is used. .TP .RI [[ / ] path/to/init.d/ ] script [ ,start= ] List of scripts which have to be added to the specified runlevels to be started with. You may use this extension to override the default values for start and stop runlevels of the script. Note that .BR lvl1 ,\ lvl2 ,\ ... are the known runlevels explained above. The extension .IR ,stop= is also possible. .TP .RI \fB\-r\fR\ [[ / ] path/to/init.d/ ] script\ ... List of scripts which should be removed from the runlevels. If a path is used it should point to the absolute or relative location of the boot scripts. .B insserv checks for the existence of these scripts. .\" .SH OVERRIDES Beside using the extensions .IR ,start= and .IR ,stop= it is possible to use override files replace a LSB comment header or simple provide a missing LSB comment header. This can be done by placing a file with the new LSB comment header using the same name as the boot or init script in the directory .IR /etc/insserv/overrides/ . For third party boot scripts without LSB header it is possible to add a file with the same name in the directory .I /usr/share/insserv/overrides/ to make them completely LSB compliant. .\" .SH UPSTART JOB COMPATIBILITY To allow upstart jobs to work as init.d scripts, insserv will recognize a symlink from path/to/init.d/script to /lib/init/upstart-job as upstart jobs, and instead of reading the header from the file will run the script with the argument lsb-header to get the script header. .SH EXIT CODES The exit codes have the following conditions: .RS 7 .IP 0 5 Service was successfully installed or removed .IP 1 5 Service was not installed or removed .RE .RS 5 .SH NOTE Please be aware that the following patterns of boot script file names will be not accepted by .BR insserv: .sp 1 .in +1l .nf @@BEGIN_SUSE@@ *.local @@END_SUSE@@ *.dpkg* *.rpm* *.ba* *.old *.new *.org *.orig *.save *.swp *.core *~ .fi .in -1l .sp 1 with the wildcard character .BR * . Beside this all boot script file names beginning with one of the following characters .sp 1 .in +1l .nf $.#%_+-\\*[]^:()~ .fi .in -1l .sp 1 will be ignored. .SH BUGS Boot scripts sometimes lack a LSB comment header. Contact a package maintainer or developer of the software which provides the script to have a LSB comment header added to it. .SH FILES .TP .I /etc/insserv.conf configuration file for .B insserv which defines the LSB System Facilities. .TP .I /etc/insserv.conf.d/ directory for further configuration files for declaring LSB System Facilities. .TP .I /etc/insserv/overrides/ path to replace existing LSB comment headers with the comment headers found in this path. .TP .I /etc/init.d/ path to the @@BEGIN_SUSE@@ SuSE @@END_SUSE@@ init script base directory as required by the Linux Standard Base Specification (LSB). .PP .IR /etc/init.d/.depend.boot , .br .IR /etc/init.d/.depend.start , .br .I /etc/init.d/.depend.stop .in +7 The .BR make (1) like dependency files produced by .B insserv for .IR booting ", " starting ", and " stopping with the help of .BR startpar (8). .in -7 .\" .SH SEE ALSO @@BEGIN_SUSE@@ .BR init.d (7), @@END_SUSE@@ .BR init (7), @@BEGIN_SUSE@@ .BR startproc (8), .BR checkproc (8), .BR killproc (8), @@END_SUSE@@ .BR startpar (8). .SH COPYRIGHT 2000\-2009 Werner Fink, .br 2009 SuSE Linux Products GmbH Nuernberg, Germany. .br 2000\-2003 SuSE GmbH Nuernberg, Germany, .br 2007\-2009 SuSE Linux Products GmbH Nuernberg, Germany. .SH AUTHOR Werner Fink .SH CONTRIBUTORS Petter Reinholdtsen .br Kel Modderman insserv-1.14.0/remove_initd0000755017777601777750000000005211260662111015312 0ustar nobodynobody#!/bin/sh exec /sbin/insserv -r ${1+"$@"} insserv-1.14.0/CHANGES0000644017777601777750000004247411366034150013714 0ustar nobodynobody------------------------------------------------------------------- Wed Apr 28 15:29:23 CEST 2010 - werner@suse.de - Use savannah as primary site ------------------------------------------------------------------- Fri Feb 19 13:36:50 CET 2010 - werner@suse.de - Adopt upstart-job patch to be able to support traditional sysv rc execution of upstart jobs. Patch from Petter Reinholdtsen. - Defend against symlinks in init.d/ to other scripts in init.d/ Patch from Kel Modderman found at the Debian SVN server. ------------------------------------------------------------------- Sat Nov 14 20:50:10 CEST 2009 - pere@hungry.com - Add simple test case checking the ordering of three scripts. Run the test suite before installing. ------------------------------------------------------------------- Wed Aug 19 12:07:58 CEST 2009 - werner@suse.de - Use dependency sorting for scripts using $all ------------------------------------------------------------------- Tue Aug 18 14:16:05 CEST 2009 - werner@suse.de - Make $all available for stopping order ------------------------------------------------------------------- Mon Aug 17 14:55:39 CEST 2009 - werner@suse.de - Loops within /etc/insserv.conf are real errors - The system facility $all is special ------------------------------------------------------------------- Thu Jul 23 11:28:08 CEST 2009 - werner@suse.de - Fix expansion of the system facilties ------------------------------------------------------------------- Mon Jun 29 14:26:57 CEST 2009 - werner@suse.de - Make it possible to mark a script as interactive in the LSB header its self (patch from Petter Reinholdtsen). ------------------------------------------------------------------- Mon Mar 9 13:12:17 CET 2009 - werner@suse.de - Add service in .depend.start even if $all only is used ------------------------------------------------------------------- Mon Feb 23 13:04:00 CET 2009 - werner@suse.de - Allow overriding level on the command line ------------------------------------------------------------------- Thu Feb 12 14:15:22 CET 2009 - werner@suse.de - Do not be fooled by inexistent /proc ------------------------------------------------------------------- Fri Sep 12 12:33:29 CEST 2008 - werner@suse.de - Don't be fooled by broken symlinks (Patch from Kel Modderman) ------------------------------------------------------------------- Thu Sep 11 15:28:29 CEST 2008 - werner@suse.de - Load first script in argument list before all other scripts. This avoids problems with loading scripts in underterministic sequence returned by readdir(3) (Patch from Kel Modderman) ------------------------------------------------------------------- Tue Aug 12 13:40:43 CEST 2008 - werner@suse.de - Mention the $null facility ------------------------------------------------------------------- Tue Jul 29 15:23:43 CEST 2008 - werner@suse.de - Resolve server minor problems on openSUSE and Debian * Handle the runlevel bit for system boot in lvl2str() * Handle dependencies on several script on command line * Let link scheme overwrite empty default start/stop tags ------------------------------------------------------------------- Wed Jul 2 19:38:15 CEST 2008 - werner@suse.de - Use prefetch from kernels code for lists - Make start/stop requirements link to the services its self - Make -pedantic work - Be sure that the code is optimized ------------------------------------------------------------------- Wed Jun 25 19:37:35 CEST 2008 - werner@suse.de - Handle provides and service lists in parallel, this should reduce the double efforts. - Calculate start oder and stop order separately - Sort targets in the makefile accordingly to their order ------------------------------------------------------------------- Wed Jun 18 15:27:16 CEST 2008 - werner@suse.de - Clean out several provides of one specific script, add therefore an alias list to the first provide. This makes less work load on following the full dependcy trees. - Use aligned memory allocation to avoid memory fragmentation - Use reference counts to be able to free not needed memory - Be able to follow not only the start but also the stop dependcies ------------------------------------------------------------------- Wed Jun 11 14:35:13 CEST 2008 - werner@suse.de - Make kbd interactive to avoid race during tty resize (bnc#259577) ------------------------------------------------------------------- Wed Jun 4 12:17:44 CEST 2008 - werner@suse.de - Scan service links even for non LSB scripts (bnc#391014) ------------------------------------------------------------------- Thu May 29 00:42:42 CEST 2008 - dmueller@suse.de - really remove last reference to boot.setclock (bnc#384254) ------------------------------------------------------------------- Tue May 20 09:25:41 CEST 2008 - werner@suse.de - Remove last occurence of boot.setclock (bnc#384254) ------------------------------------------------------------------- Thu May 15 13:42:57 CEST 2008 - werner@suse.de - Also ignore temporary (mostly?) errors if executed within an rpm scriptlet (bnc#385498, bnc#384254) - Move stat() check for /etc/insserv.conf.d/ configure files to scan_conf() otherwise we never scan those files. ------------------------------------------------------------------- Mon May 5 18:47:26 CEST 2008 - werner@suse.de - Ignore temporary errors during update with YaST/zypper (bnc#385498) ------------------------------------------------------------------- Mon Apr 28 15:25:54 CEST 2008 - werner@suse.de - boot.clock was into two scripts for boot and shutdown Todo: make insserv knowing about Required-Stop to merge them again to one boot.clock. ------------------------------------------------------------------- Wed Apr 9 16:02:26 CEST 2008 - mkoenig@suse.de - add boot.crypto-early to insserv.conf ------------------------------------------------------------------- Mon Feb 4 18:32:33 CET 2008 - werner@suse.de - Expand system facilities to make deep of the dependcy graph less deeper. ------------------------------------------------------------------- Fri Feb 1 14:34:52 CET 2008 - werner@suse.de - Avoid SIGSEGV in functions which can handle NULL pointers thanks goes to Petter Reinholdtsen for his report ------------------------------------------------------------------- Wed Jan 30 17:49:55 CET 2008 - werner@suse.de - New version 1.11.0 of insserv - Code cleanup, update copyrights, and manual pages - Use __attribute__ of gcc for better function management - Use __attribute__ of gcc for alignment of list_t pointers - Some preparation listing.c for kill link sorting (TODO) ------------------------------------------------------------------- Thu Jan 24 18:59:14 CET 2008 - werner@suse.de - Add and integrate many patches from Debian svn tree done by Petter Reinholdtsen * Make it possible to set the path at runtime, to make it easier to write test suites * Support for reading LSB headers info from override directory * Accept script names like 'rc.local' for Debian build * Use other defaults on Debian systems (start, stop levels) * Put redundant level informations in one API * Fix the handling of stop scripts and the shutdown sequence on Debian systems * Better loop report * Make loops fatal if not forced - Clean the API for listing the services ------------------------------------------------------------------- Wed Oct 10 12:39:25 CEST 2007 - werner@suse.de - Even disabled scripts should be occur in dependcies (#331615) - Handle return values of strsep in case of several provides - Do not scan services links if removed later on - New version 1.10.0 of insserv ------------------------------------------------------------------- Fri Aug 31 16:08:47 CEST 2007 - werner@suse.de - Scan all scripts for Start-Before even if already known (#297214) - Do not add disabled scripts to the depend files ------------------------------------------------------------------- Fri Jul 13 12:05:19 CEST 2007 - werner@suse.de - Remove hotplug and pcmcia from insserv.conf because they are dropped (bug #291417) ------------------------------------------------------------------- Tue Jun 19 18:59:30 CEST 2007 - werner@suse.de - Scan all files in `should start before' even facilities - Read insserv.conf in other root environments ------------------------------------------------------------------- Tue May 29 17:45:06 CEST 2007 - werner@suse.de - Ignore rcs-files (bug #278520) ------------------------------------------------------------------- Mon Jan 29 15:08:17 CET 2007 - werner@suse.de - Split insserv.conf off from source tar ball to avoid patching - Add boot.crypto to $local_fs - Add smbfs/cifs to $remote_fs ------------------------------------------------------------------- Mon Jan 22 15:17:23 CET 2007 - werner@suse.de - Add missed `start this script before' feature patch (fate#301269) ------------------------------------------------------------------- Tue Jan 16 14:04:06 CET 2007 - werner@suse.de - Remove obsolate `$netdaemons' facility (#209380) - Add `start this script before' feature (fate #301269) - New version 1.09.0 of insserv ------------------------------------------------------------------- Mon Nov 20 11:42:40 CET 2006 - werner@suse.de - Expand aliases even for services which requires $all (#216202) ------------------------------------------------------------------- Mon May 15 12:56:27 CEST 2006 - werner@suse.de - Make kdump boot script a interactive script to enforce that this script runs not in parallel with other script (#175340, #171332) ------------------------------------------------------------------- Wed Mar 8 17:06:47 CET 2006 - werner@suse.de - Ignore .orig and .org file (bug #155944) ------------------------------------------------------------------- Wed Mar 1 12:51:17 CET 2006 - werner@suse.de - Add a few lines about $all into the man page (bug #151561) ------------------------------------------------------------------- Mon Feb 6 16:40:46 CET 2006 - werner@suse.de - Handle duplets even for interactive scripts ------------------------------------------------------------------- Tue Jan 31 15:06:53 CET 2006 - werner@suse.de - Be sure to find all interactive scripts and set their unique start number. This solves the problem of two interactive scripts in the same start order. ------------------------------------------------------------------- Tue Apr 4 18:23:24 CEST 2006 - werner@suse.de - Add patches from Petter Reinholdtsen - Make debian part work ------------------------------------------------------------------- Wed Jan 25 14:52:26 CET 2006 - werner@suse.de - Fix the broken fix (bug #145403) ------------------------------------------------------------------- Mon Jan 23 13:35:40 CET 2006 - werner@suse.de - Make missing Provides and Requires non-fatal. ------------------------------------------------------------------- Fri Jan 20 18:13:39 CET 2006 - werner@suse.de - Fix bug in handling of non-LSB scripts - Add error condition for broken LSB scripts - Make calculation of order number somewhat smarter, e.g. do not count system facilities. ------------------------------------------------------------------- Thu Jan 19 15:33:06 CET 2006 - werner@suse.de - Make the restore option work even on broken link scheme - Don't count empty provides ------------------------------------------------------------------- Thu Nov 10 18:05:53 CET 2005 - werner@suse.de - Add patches from Petter Reinholdtsen * Avoid zero pointer * Allow not existing rc ditrectories at dryrun * Some more debugging code * Map the runlevel scheme into a common struct ------------------------------------------------------------------- Fri Oct 28 17:48:38 CEST 2005 - werner@suse.de - Fix duplet handling in make like service handling (bug #130451) ------------------------------------------------------------------- Thu Sep 15 16:54:40 CEST 2005 - werner@suse.de - Add dryrun changes from Petter Reinholdtsen - First step for support of traditional SystemV link scheme ------------------------------------------------------------------- Wed May 25 17:33:30 CEST 2005 - werner@suse.de - Include confdir patch from Ludwig Nussel - Bounce version to 1.01.0 ------------------------------------------------------------------- Mon Nov 29 16:32:04 CET 2004 - werner@suse.de - Add some comments about boot script file names. ------------------------------------------------------------------- Thu Nov 25 18:24:35 CET 2004 - werner@suse.de - Update to 1.00.8 : use correct listing head (bug #48415) ------------------------------------------------------------------- Thu Nov 25 13:48:45 CET 2004 - werner@suse.de - Update to 1.00.7 : be more verbose on invalid file names ------------------------------------------------------------------- Tue Nov 23 13:13:35 CET 2004 - werner@suse.de - Update to 1.00.6 ------------------------------------------------------------------- Tue Nov 23 13:00:22 CET 2004 - werner@suse.de - Fix segmentation fault caused by broken cast on reversed scanned linkage structure (bug #48415) ------------------------------------------------------------------- Wed Sep 22 11:52:43 CEST 2004 - werner@suse.de - Do not call error recursively if chdir fails (bugzilla #45767) ------------------------------------------------------------------- Mon Sep 20 16:40:13 CEST 2004 - werner@suse.de - Add a few lines about the make like dependency files to the manual page of insserv. ------------------------------------------------------------------- Fri Sep 17 12:16:04 CEST 2004 - werner@suse.de - Boot scripts which may call sulogin are INTERACTIVE ------------------------------------------------------------------- Thu Sep 16 14:19:56 CEST 2004 - werner@suse.de - Add dnsmasq and lwresd as optional to system facility named ------------------------------------------------------------------- Thu Sep 2 11:34:09 BST 2004 - werner@suse.de - Fix dependency of boot.clock also used in single mode (bug#44610) ------------------------------------------------------------------- Fri Aug 27 17:50:39 CEST 2004 - werner@suse.de - Fix dependencies of single script - Fix $ALL handling for dependency files - Fix handling of interactive scripts for for dependency files ------------------------------------------------------------------- Thu Aug 19 17:37:06 CEST 2004 - werner@suse.de - Update to 1.00.3 to support dependency files for make calls ------------------------------------------------------------------- Wed Mar 31 11:52:31 CEST 2004 - werner@suse.de - Verbose option and explain exit status in manual page (#37599) ------------------------------------------------------------------- Thu Mar 18 17:53:04 CET 2004 - werner@suse.de - Remove debug message ------------------------------------------------------------------- Wed Mar 17 15:12:55 CET 2004 - werner@suse.de - Implement the `$all' feature (bug #36140) ------------------------------------------------------------------- Wed Mar 10 17:27:53 CET 2004 - werner@suse.de - YAL (Yet Another Loop) fixed (bug #35522) ------------------------------------------------------------------- Thu Feb 19 18:40:36 CET 2004 - werner@suse.de - Do not create a K* link if no S* link exist. ------------------------------------------------------------------- Thu Feb 19 13:55:14 CET 2004 - werner@suse.de - More about K* links in /etc/init.d/boot.d ------------------------------------------------------------------- Tue Feb 17 14:18:30 CET 2004 - ro@suse.de - enable K* (kill) links in /etc/init.d/boot.d ------------------------------------------------------------------- Wed Oct 1 17:58:08 CEST 2003 - werner@suse.de - Allow numbers in initial segment of namespace of scripts (#31793) ------------------------------------------------------------------- Mon Sep 22 18:41:26 CEST 2003 - werner@suse.de - Stop recursive walk on dependency tree hard if a loop is detected ------------------------------------------------------------------- Tue Sep 16 13:57:59 CEST 2003 - werner@suse.de - Add extra flag for ENABLED status of services due the level can not used for checking this anymore (bug #31000) ------------------------------------------------------------------- Mon Sep 1 13:49:23 CEST 2003 - werner@suse.de - Move (re)calculation of order of active scripts after the calculation of all start orders to hold dependencies unique ------------------------------------------------------------------- Fri Aug 29 14:42:22 CEST 2003 - werner@suse.de - Update to 1.00.0 which fixes the handling of interactive services for passphrase input (bug #29375) and enhance the calculation of already enabled NONE-LSB scripts. ------------------------------------------------------------------- Thu Jul 3 14:53:39 CEST 2003 - werner@suse.de - Follow LSB specs and specify script functions for proc handling (no /sbin in PATH anymore). ------------------------------------------------------------------- Wed Jun 18 14:39:47 CEST 2003 - werner@suse.de - Update to 0.99.9: better handling with not LSB conform scripts ------------------------------------------------------------------- Thu Jun 12 10:47:07 CEST 2003 - kukuk@suse.de - fix filelist ------------------------------------------------------------------- Wed Apr 16 18:31:07 CEST 2003 - werner@suse.de - Update to 0.99.8: be able to remove doubles with -rf ------------------------------------------------------------------- Sat Mar 8 16:00:23 CET 2003 - kukuk@suse.de - Add /lib/lsb back [Bug #24904] ------------------------------------------------------------------- Wed Jan 15 17:07:54 CET 2003 - ro@suse.de - split from aaa_base insserv-1.14.0/COPYING0000644017777601777750000004310311260662111013737 0ustar nobodynobody GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. insserv-1.14.0/init-functions0000644017777601777750000000127111260662111015600 0ustar nobodynobody# # Define init LSB shell functions # # # Source SuSE's rc functions # . /etc/rc.status # # Be sure that start_daemon, killproc, and # pidofproc will be script functions. # function start_daemon () { /sbin/start_daemon ${1+"$@"} } function killproc () { /sbin/killproc ${1+"$@"} } function pidofproc () { /sbin/pidofproc ${1+"$@"} } # # Logging of succes messages # function log_success_msg () { echo -en "$@" echo -e "$rc_done" } # # Logging of failure messages # function log_failure_msg () { echo -en "$@" echo -e "$rc_failed" } # # Logging of warn messages # function log_warning_msg () { echo -en "$@" echo -e "${stat}${attn} warning${norm}" } insserv-1.14.0/insserv.c0000644017777601777750000030025711337506464014564 0ustar nobodynobody/* * insserv(.c) * * Copyright 2000-2009 Werner Fink, 2000 SuSE GmbH Nuernberg, Germany, * 2003 SuSE Linux AG, Germany. * 2004 SuSE LINUX AG, Germany. * 2005-2009 SUSE LINUX Products GmbH, Germany. * Copyright 2005,2008,2009 Petter Reinholdtsen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #define MINIMAL_MAKE 1 /* Remove disabled scripts from .depend.boot, * .depend.start, .depend.halt, and .depend.stop */ #define MINIMAL_RULES 1 /* ditto */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(USE_RPMLIB) && (USE_RPMLIB > 0) # include # include #endif /* USE_RPMLIB */ #ifdef SUSE # include #endif /* SUSE */ #include "listing.h" #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 # ifndef POSIX_FADV_SEQUENTIAL # define posix_fadvise(fd, off, len, adv) (-1) # endif #endif #ifndef PATH_MAX # ifdef MAXPATHLEN # define PATH_MAX MAXPATHLEN # else # define PATH_MAX 2048 # endif #endif #ifdef SUSE # define DEFAULT_START_LVL "3 5" # define DEFAULT_STOP_LVL "3 5" # define USE_KILL_IN_BOOT 1 # define USE_COMPAT_EMPTY 1 static inline void oneway(char *restrict stop) attribute((always_inline,nonnull(1))); static inline void oneway(char *restrict stop) { char * ptr = stop; while ((ptr = strpbrk(ptr, "016sS"))) *ptr++ = ' '; } #else /* not SUSE, but Debian */ # define DEFAULT_START_LVL "2 3 4 5" # define DEFAULT_STOP_LVL "0 1 6" # define DEFAULT_DEPENDENCY "$remote_fs $syslog" # undef USE_KILL_IN_BOOT # undef USE_COMPAT_EMPTY #endif /* not SUSE, but Debian */ #ifndef INITDIR # define INITDIR "/etc/init.d" #endif #ifndef OVERRIDEDIR # define OVERRIDEDIR "/etc/insserv/overrides" #endif #ifndef INSCONF # define INSCONF "/etc/insserv.conf" #endif const char *upstartjob_path = "/lib/init/upstart-job"; /* * For a description of regular expressions see regex(7). */ #define COMM "^#[[:blank:]]*" #define VALUE ":[[:blank:]]*([[:print:]]*)" /* The second substring contains our value (the first is all) */ #define SUBNUM 2 #define SUBNUM_SHD 3 #define START "[-_]+start" #define STOP "[-_]+stop" /* The main regular search expressions */ #define PROVIDES COMM "provides" VALUE #define REQUIRED COMM "required" #define SHOULD COMM "(x[-_]+[a-z0-9_-]*)?should" #define BEFORE COMM "(x[-_]+[a-z0-9_-]*)?start[-_]+before" #define AFTER COMM "(x[-_]+[a-z0-9_-]*)?stop[-_]+after" #define DEFAULT COMM "default" #define REQUIRED_START REQUIRED START VALUE #define REQUIRED_STOP REQUIRED STOP VALUE #define SHOULD_START SHOULD START VALUE #define SHOULD_STOP SHOULD STOP VALUE #define START_BEFORE BEFORE VALUE #define STOP_AFTER AFTER VALUE #define DEFAULT_START DEFAULT START VALUE #define DEFAULT_STOP DEFAULT STOP VALUE #define DESCRIPTION COMM "description" VALUE #define INTERACTIVE COMM "(x[-_]+[a-z0-9_-]*)?interactive" VALUE /* System facility search within /etc/insserv.conf */ #define EQSIGN "([[:blank:]]*[=:][[:blank:]]*|[[:blank:]]+)" #define CONFLINE "^(\\$[a-z0-9_-]+)" EQSIGN "([[:print:]]*)" #define CONFLINE2 "^(<[a-z0-9_-]+>)" EQSIGN "([[:print:]]*)" #define SUBCONF 2 #define SUBCONFNUM 4 /* The root file system */ static char *root; /* The main line buffer if unique */ static char buf[LINE_MAX]; /* When to be verbose, and what level of verbosity */ static int verbose = 0; /* When to be verbose */ static boolean dryrun = false; /* When paths set do not add root if any */ static boolean set_override = false; static boolean set_insconf = false; /* Search results points here */ typedef struct lsb_struct { char *provides; char *required_start; char *required_stop; char *should_start; char *should_stop; char *start_before; char *stop_after; char *default_start; char *default_stop; char *description; char *interactive; } attribute((aligned(sizeof(char*)))) lsb_t; /* Search results points here */ typedef struct reg_struct { regex_t prov; regex_t req_start; regex_t req_stop; regex_t shl_start; regex_t shl_stop; regex_t start_bf; regex_t stop_af; regex_t def_start; regex_t def_stop; regex_t desc; regex_t interact; } attribute((aligned(sizeof(regex_t)))) reg_t; typedef struct creg_struct { regex_t isysfaci; regex_t isactive; } attribute((aligned(sizeof(regex_t)))) creg_t; static lsb_t script_inf; static reg_t reg; static creg_t creg; static char empty[1] = ""; /* Delimeters used for spliting results with strsep(3) */ const char *const delimeter = " ,;\t"; /* * push and pop directory changes: pushd() and popd() */ typedef struct pwd_struct { list_t deep; char *pwd; } __align pwd_t; #define getpwd(list) list_entry((list), struct pwd_struct, deep) static list_t pwd = { &pwd, &pwd }, * topd = &pwd; static void pushd(const char *restrict const path) attribute((nonnull(1))); static void pushd(const char *restrict const path) { pwd_t *restrict dir; if (posix_memalign((void*)&dir, sizeof(void*), alignof(pwd_t)) == 0) { if (!(dir->pwd = getcwd((char*)0, 0))) goto err; insert(&dir->deep, topd->prev); if (chdir(path) < 0) goto err; return; } err: error ("pushd() can not change to directory %s: %s\n", path, strerror(errno)); } static void popd(void) { pwd_t * dir; if (list_empty(topd)) goto out; dir = getpwd(topd->prev); if (chdir(dir->pwd) < 0) error ("popd() can not change directory %s: %s\n", dir->pwd, strerror(errno)); delete(topd->prev); free(dir->pwd); free(dir); out: return; } /* * Linked list of system facilities services and their replacment */ typedef struct string { int *restrict ref; char *name; } __align string_t; typedef struct repl { list_t r_list; string_t r[1]; ushort flags; } __align repl_t; #define getrepl(arg) list_entry((arg), struct repl, r_list) typedef struct faci { list_t list; list_t replace; char *name; } __align faci_t; #define getfaci(arg) list_entry((arg), struct faci, list) static list_t sysfaci = { &sysfaci, &sysfaci }, *sysfaci_start = &sysfaci; /* * Remember requests for required or should services and expand `$' token */ static void rememberreq(service_t *restrict serv, uint bit, const char *restrict required) attribute((noinline,nonnull(1,3))); static void rememberreq(service_t * restrict serv, uint bit, const char * restrict required) { const char type = (bit & REQ_KILL) ? 'K' : 'S'; const char * token; char * tmp = strdupa(required); list_t * ptr, * list; ushort old = bit; if (!tmp) error("%s", strerror(errno)); while ((token = strsep(&tmp, delimeter)) && *token) { service_t * req, * here, * need; boolean found = false; bit = old; switch(*token) { case '+': /* This is an optional token */ token++; bit &= ~REQ_MUST; bit |= REQ_SHLD; default: req = addservice(token); if (bit & REQ_KILL) { req = getorig(req); list = &req->sort.rev; here = req; need = serv; } else { serv = getorig(serv); list = &serv->sort.req; here = serv; need = req; } np_list_for_each(ptr, list) { if (!strcmp(getreq(ptr)->serv->name, need->name)) { getreq(ptr)->flags |= bit; found = true; break; } } if (!found) { req_t *restrict this; if (posix_memalign((void*)&this, sizeof(void*), alignof(req_t)) != 0) error("%s", strerror(errno)); memset(this, 0, alignof(req_t)); insert(&this->list, list->prev); this->flags = bit; this->serv = need; } /* Expand requested services for sorting */ requires(here, need, type); break; case '$': if (strcasecmp(token, "$null") == 0) break; if (strcasecmp(token, "$all") == 0) { if (bit & REQ_KILL) serv->attr.flags |= SERV_FIRST; else serv->attr.flags |= SERV_ALL; break; } /* Expand the `$' token recursively down */ list_for_each(ptr, sysfaci_start) { if (!strcmp(token, getfaci(ptr)->name)) { list_t * lst; np_list_for_each(lst, &getfaci(ptr)->replace) rememberreq(serv, bit, getrepl(lst)->r[0].name); break; } } break; } } } static void reversereq(service_t *restrict serv, uint bit, const char *restrict list) attribute((noinline,nonnull(1,3))); static void reversereq(service_t *restrict serv, uint bit, const char *restrict list) { const char * token; char * tmp = strdupa(list); ushort old = bit; if (!tmp) error("%s", strerror(errno)); while ((token = strsep(&tmp, delimeter)) && *token) { service_t * rev; list_t * ptr; bit = old; switch (*token) { case '+': token++; bit &= ~REQ_MUST; bit |= REQ_SHLD; default: rev = addservice(token); rememberreq(rev, bit, serv->name); break; case '$': list_for_each(ptr, sysfaci_start) { if (!strcmp(token, getfaci(ptr)->name)) { list_t * lst; np_list_for_each(lst, &getfaci(ptr)->replace) reversereq(serv, bit, getrepl(lst)->r[0].name); break; } } break; } } } /* * Check required services for name */ static boolean chkrequired(service_t *restrict serv) attribute((nonnull(1))); static boolean chkrequired(service_t *restrict serv) { boolean ret = true; list_t * pos; if (!serv) goto out; serv = getorig(serv); np_list_for_each(pos, &serv->sort.req) { req_t *req = getreq(pos); service_t * must; if ((req->flags & REQ_MUST) == 0) continue; must = req->serv; must = getorig(must); if ((must->attr.flags & (SERV_CMDLINE|SERV_ENABLED)) == 0) { warn("Service %s has to be enabled to start service %s\n", req->serv->name, serv->name); ret = false; } } #if 0 if (serv->attr.flags & (SERV_CMDLINE|SERV_ENABLED)) goto out; np_list_for_each(pos, &serv->sort.rev) { req_t *rev = getreq(pos); service_t * must; if ((rev->flags & REQ_MUST) == 0) continue; must = rev->serv; must = getorig(must); if (must->attr.flags & (SERV_CMDLINE|SERV_ENABLED)) { warn("Service %s has to be enabled to stop service %s\n", serv->name, rev->serv->name); ret = false; } } #endif out: return ret; } /* * Check dependencies for name as a service */ static boolean chkdependencies(service_t *restrict serv) attribute((nonnull(1))); static boolean chkdependencies(service_t *restrict serv) { const char * const name = serv->name; boolean ret = true; list_t * ptr; list_for_each(ptr, s_start) { service_t * cur = getservice(ptr); list_t * pos; if (!cur) continue; if ((cur->attr.flags & SERV_ENABLED) == 0) continue; if (cur->attr.flags & SERV_DUPLET) continue; if (list_empty(&cur->sort.req)) continue; np_list_for_each(pos, &cur->sort.req) { req_t *req = getreq(pos); const ushort flags = req->serv->attr.flags; if (!(req->flags & REQ_MUST)) continue; if (strcmp(req->serv->name, name) != 0) continue; if ((cur->attr.flags & SERV_CMDLINE) && (flags & SERV_CMDLINE)) continue; warn("Service %s has to be enabled to start service %s\n", name, cur->name); ret = false; } } return ret; } /* * This helps us to work out the current symbolic link structure */ static inline service_t * current_structure(const char *const restrict this, const char order, const int runlvl, const char type) attribute((always_inline,nonnull(1))); static inline service_t * current_structure(const char *const this, const char order, const int runlvl, const char type) { service_t * serv = addservice(this); level_t * run; ushort here = map_runlevel_to_lvl(runlvl); if (type == 'K') { run = serv->stopp; if (!serv->attr.korder) serv->attr.korder = 99; if (serv->attr.korder > order) serv->attr.korder = order; #ifdef SUSE /* This because SuSE boot script concept uses a differential link scheme. */ here &= ~LVL_ONEWAY; #endif /* SUSE */ } else { run = serv->start; if (serv->attr.sorder < order) serv->attr.sorder = order; } run->lvl |= here; return serv; } static void setlsb(const char *restrict const name) attribute((unused)); static void setlsb(const char *restrict const name) { service_t * serv = findservice(name); if (serv) serv->attr.flags &= ~SERV_NOTLSB; } /* * This helps us to set none LSB conform scripts to required * max order, therefore we set a dependency to the first * lsb conform service found in current link scheme. */ static inline void nonlsb_script(void) attribute((always_inline)); static inline void nonlsb_script(void) { list_t * pos; list_for_each(pos, s_start) { if (getservice(pos)->attr.flags & SERV_NOTLSB) { service_t * req, * srv = getservice(pos); list_t * tmp; uchar max; max = 0; req = (service_t*)0; list_for_each(tmp, s_start) { service_t * cur = getservice(tmp); if (cur->attr.flags & SERV_NOTLSB) continue; if ((cur->attr.flags & SERV_ENABLED) == 0) continue; if (!cur->attr.sorder) continue; if ((srv->start->lvl & cur->start->lvl) == 0) continue; if (cur->attr.sorder >= srv->attr.sorder) continue; if (max < cur->attr.sorder) { max = cur->attr.sorder; req = cur; } } if (req) requires(srv, req, 'S'); max = 99; req = (service_t*)0; list_for_each(tmp, s_start) { service_t * cur = getservice(tmp); if (cur->attr.flags & SERV_NOTLSB) continue; if ((cur->attr.flags & SERV_ENABLED) == 0) continue; if (!cur->attr.korder) continue; if ((srv->stopp->lvl & cur->stopp->lvl) == 0) continue; if (cur->attr.korder <= srv->attr.korder) continue; if (max > cur->attr.korder) { max = cur->attr.korder; req = cur; } } if (req) requires(req, srv, 'K'); } } } /* * This helps us to get interactive scripts to be the only service * within on start or stop service group. Remaining problem is that * if required scripts are missed the order can be wrong. */ static inline void active_script(void) attribute((always_inline)); static inline void active_script(void) { list_t * pos; int deep = 1; for (deep = 0; deep < 100; deep++) { list_for_each(pos, s_start) { service_t * serv = getservice(pos); list_t * tmp; if (serv->attr.script == (char*)0) continue; if ((serv->attr.flags & SERV_INTRACT) == 0) continue; serv->attr.sorder = getorder(serv->attr.script, 'S'); if (serv->attr.sorder != deep) continue; if (serv->attr.flags & SERV_DUPLET) continue; /* Duplet */ list_for_each(tmp, s_start) { service_t * cur = getservice(tmp); const char * script; if (getorig(cur) == serv) continue; if ((serv->start->lvl & cur->start->lvl) == 0) continue; /* * Use real script name for getorder()/setorder() */ if (cur->attr.script == (char*)0) continue; script = cur->attr.script; cur->attr.sorder = getorder(script, 'S'); if (cur->attr.sorder != deep) continue; /* * Increase order of members of the same start * group and recalculate dependency order (`true') */ setorder(script, 'S', ++cur->attr.sorder, true); } } } } /* * Last but not least the `$all' scripts will be set to the * beginning of the future stop order respectivly to the * end of the future start order. */ static inline void all_script(void) attribute((always_inline)); static inline void all_script(void) { list_t * pos; list_for_each(pos, s_start) { service_t * serv = getservice(pos); list_t * tmp; if (serv->attr.flags & SERV_DUPLET) continue; /* Duplet */ if (!(serv->attr.flags & SERV_FIRST)) continue; if (serv->attr.script == (char*)0) continue; list_for_each(tmp, s_start) { service_t * cur = getservice(tmp); if (cur->attr.flags & SERV_DUPLET) continue; /* Duplet */ if ((serv->stopp->lvl & cur->stopp->lvl) == 0) continue; if (cur == serv) continue; if (cur->attr.flags & SERV_FIRST) continue; rememberreq(serv, REQ_SHLD|REQ_KILL, cur->name); } setorder(serv->attr.script, 'K', 1, false); } list_for_each(pos, s_start) { service_t * serv = getservice(pos); list_t * tmp; if (serv->attr.flags & SERV_DUPLET) continue; /* Duplet */ if (!(serv->attr.flags & SERV_ALL)) continue; if (serv->attr.script == (char*)0) continue; list_for_each(tmp, s_start) { service_t * cur = getservice(tmp); if (cur->attr.flags & SERV_DUPLET) continue; /* Duplet */ if ((serv->start->lvl & cur->start->lvl) == 0) continue; if (cur == serv) continue; if (cur->attr.flags & SERV_ALL) continue; rememberreq(serv, REQ_SHLD, cur->name); } } } /* * Make the dependency files */ static inline void makedep(void) attribute((always_inline)); static inline void makedep(void) { FILE *boot, *start, *stop, *out; #ifdef USE_KILL_IN_BOOT FILE *halt; #endif /* USE_KILL_IN_BOOT */ const char *target; service_t *serv; if (dryrun) { #ifdef USE_KILL_IN_BOOT info(1, "dryrun, not creating .depend.boot, .depend.start, .depend.halt, and .depend.stop\n"); #else /* not USE_KILL_IN_BOOT */ info(1, "dryrun, not creating .depend.boot, .depend.start, and .depend.stop\n"); #endif /* not USE_KILL_IN_BOOT */ return; } if (!(boot = fopen(".depend.boot", "w"))) { warn("fopen(.depend.stop): %s\n", strerror(errno)); return; } if (!(start = fopen(".depend.start", "w"))) { warn("fopen(.depend.start): %s\n", strerror(errno)); fclose(boot); return; } info(1, "creating .depend.boot\n"); info(1, "creating .depend.start\n"); lsort('S'); /* Sort into start order, set new sorder */ target = (char*)0; fprintf(boot, "TARGETS ="); while ((serv = listscripts(&target, 'S', LVL_BOOT))) { if (!serv) continue; #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0) if (serv->attr.ref <= 0) continue; #endif /* MINIMAL_MAKE */ fprintf(boot, " %s", target); } fputc('\n', boot); target = (char*)0; fprintf(start, "TARGETS ="); while ((serv = listscripts(&target, 'S', LVL_ALL))) { /* LVL_ALL: nearly all but not BOOT */ if (!serv) continue; #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0) if (serv->attr.ref <= 0) continue; #endif /* MINIMAL_MAKE */ fprintf(start, " %s", target); } fputc('\n', start); fprintf(boot, "INTERACTIVE ="); fprintf(start, "INTERACTIVE ="); target = (char*)0; while ((serv = listscripts(&target, 'S', LVL_BOOT|LVL_ALL))) { if (!serv) continue; #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0) if (serv->attr.ref <= 0) continue; #endif /* not MINIMAL_MAKE */ if (list_empty(&serv->sort.req)) continue; if (serv->start->lvl & LVL_BOOT) out = boot; else out = start; if (serv->attr.flags & SERV_INTRACT) fprintf(out, " %s", target); } fputc('\n', boot); fputc('\n', start); target = (char*)0; while ((serv = listscripts(&target, 'S', LVL_BOOT|LVL_ALL))) { boolean mark; list_t * pos; if (!serv) continue; #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0) if (serv->attr.ref <= 0) continue; #endif /* not MINIMAL_RULES */ if (serv->start->lvl & LVL_BOOT) out = boot; else out = start; if (list_empty(&serv->sort.req)) continue; mark = false; np_list_for_each(pos, &serv->sort.req) { req_t * req = getreq(pos); service_t * dep = req->serv; const char * name; if (!dep) continue; if (dep->attr.flags & SERV_DUPLET) continue; #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0) if (dep->attr.ref <= 0) continue; #endif /* not MINIMAL_RULES */ /* * No self dependcies or from the last */ if (dep == serv || (dep->attr.flags & SERV_ALL)) continue; if ((serv->start->lvl & dep->start->lvl) == 0) continue; if ((name = dep->attr.script) == (char*)0) continue; if (!mark) { fprintf(out, "%s:", target); mark = true; } fprintf(out, " %s", name); } if (mark) fputc('\n', out); } fclose(boot); fclose(start); if (!(stop = fopen(".depend.stop", "w"))) { warn("fopen(.depend.stop): %s\n", strerror(errno)); return; } #ifdef USE_KILL_IN_BOOT if (!(halt = fopen(".depend.halt", "w"))) { warn("fopen(.depend.start): %s\n", strerror(errno)); fclose(stop); return; } info(1, "creating .depend.halt\n"); #endif /* USE_KILL_IN_BOOT */ info(1, "creating .depend.stop\n"); lsort('K'); /* Sort into stop order, set new korder */ target = (char*)0; fprintf(stop, "TARGETS ="); while ((serv = listscripts(&target, 'K', LVL_NORM))) { /* LVL_NORM: nearly all but not BOOT and not SINGLE */ if (!serv) continue; #if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0) if (serv->attr.ref <= 0) continue; #endif /* MINIMAL_MAKE */ fprintf(stop, " %s", target); } fputc('\n', stop); #ifdef USE_KILL_IN_BOOT target = (char*)0; fprintf(halt, "TARGETS ="); while ((serv = listscripts(&target, 'K', LVL_BOOT))) { if (!serv) continue; # if defined(MINIMAL_MAKE) && (MINIMAL_MAKE != 0) if (serv->attr.ref <= 0) continue; # endif /* MINIMAL_MAKE */ fprintf(halt, " %s", target); } fputc('\n', halt); #endif /* USE_KILL_IN_BOOT */ target = (char*)0; while ((serv = listscripts(&target, 'K', (LVL_NORM|LVL_BOOT)))) { boolean mark; list_t * pos; if (!serv) continue; #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0) if (serv->attr.ref <= 0) continue; #endif /* not MINIMAL_RULES */ if (list_empty(&serv->sort.rev)) continue; if (serv->stopp->lvl & LVL_BOOT) #ifdef USE_KILL_IN_BOOT out = halt; else #else /* not USE_KILL_IN_BOOT */ continue; #endif /* not USE_KILL_IN_BOOT */ out = stop; mark = false; np_list_for_each(pos, &serv->sort.rev) { req_t * rev = getreq(pos); service_t * dep = rev->serv; const char * name; if (!dep) continue; if (dep->attr.flags & (SERV_DUPLET|SERV_NOSTOP)) continue; /* Duplet or no stop link */ #if defined(MINIMAL_RULES) && (MINIMAL_RULES != 0) if (dep->attr.ref <= 0) continue; #endif /* not MINIMAL_RULES */ if ((serv->stopp->lvl & dep->stopp->lvl) == 0) continue; if ((name = dep->attr.script) == (char*)0) continue; if (!mark) { fprintf(out, "%s:", target); mark = true; } fprintf(out, " %s", name); } if (mark) fputc('\n', out); } #ifdef USE_KILL_IN_BOOT fclose(halt); #endif /* USE_KILL_IN_BOOT */ fclose(stop); } /* * Internal logger */ char *myname = (char*)0; static void _logger (const char *restrict const fmt, va_list ap); static void _logger (const char *restrict const fmt, va_list ap) { extension char buf[strlen(myname)+2+strlen(fmt)+1]; strcat(strcat(strcpy(buf, myname), ": "), fmt); vfprintf(stderr, buf, ap); return; } /* * Cry and exit. */ void error (const char *restrict const fmt, ...) { static char called; va_list ap; if (called++) exit (1); va_start(ap, fmt); _logger(fmt, ap); va_end(ap); popd(); exit (1); } /* * Warn the user. */ void warn (const char *restrict const fmt, ...) { va_list ap; va_start(ap, fmt); _logger(fmt, ap); va_end(ap); return; } /* * Print message when verbose is enabled */ void info(int level, const char *fmt, ...) { va_list ap; if (level > verbose) goto out; va_start(ap, fmt); _logger(fmt, ap); va_end(ap); out: return; } /* * Check for script in list. */ static int curr_argc = -1; static inline boolean chkfor(const char *restrict const script, char **restrict const list, const int cnt) attribute((nonnull(1,2))); static inline boolean chkfor(const char *restrict const script, char **restrict const list, const int cnt) { boolean isinc = false; register int c = cnt; curr_argc = -1; while (c--) { if (*script != *list[c]) continue; if (!strcmp(script, list[c])) { isinc = true; curr_argc = c; break; } } return isinc; } /* * Open a runlevel directory, if it not * exists than create one. */ static DIR * openrcdir(const char *restrict const rcpath) attribute((nonnull(1))); static DIR * openrcdir(const char *restrict const rcpath) { DIR * rcdir; struct stat st; int dfd; if (stat(rcpath, &st) < 0) { if (errno == ENOENT) { info(1, "creating directory '%s'\n", rcpath); if (!dryrun) mkdir(rcpath, (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)); } else error("can not stat(%s): %s\n", rcpath, strerror(errno)); } if ((rcdir = opendir(rcpath)) == (DIR*)0) { if (dryrun) warn ("can not opendir(%s): %s\n", rcpath, strerror(errno)); else error("can not opendir(%s): %s\n", rcpath, strerror(errno)); } #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 else if ((dfd = dirfd(rcdir)) != 0) { (void)posix_fadvise(dfd, 0, 0, POSIX_FADV_WILLNEED); (void)posix_fadvise(dfd, 0, 0, POSIX_FADV_SEQUENTIAL); } #endif return rcdir; } /* * Wrapper for regcomp(3) */ static inline void regcompiler(regex_t *restrict preg, const char *restrict regex, int cflags) attribute((always_inline,nonnull(1,2))); static inline void regcompiler(regex_t *restrict preg, const char *restrict regex, int cflags) { register int ret = regcomp(preg, regex, cflags); if (ret) { regerror(ret, preg, buf, sizeof (buf)); regfree (preg); error("%s\n", buf); } return; } /* * Wrapper for regexec(3) */ static inline boolean regexecutor(regex_t *restrict preg, const char *restrict string, size_t nmatch, regmatch_t pmatch[], int eflags) attribute((nonnull(1,2))); static inline boolean regexecutor(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags) { register int ret = regexec(preg, string, nmatch, pmatch, eflags); if (ret > REG_NOMATCH) { regerror(ret, preg, buf, sizeof (buf)); regfree (preg); warn("%s\n", buf); } return (ret ? false : true); } /* * The script scanning engine. * We have to alloc the regular expressions first before * calling scan_script_defaults(). After the last call * of scan_script_defaults() we may free the expressions. */ static inline void scan_script_regalloc(void) attribute((always_inline)); static inline void scan_script_regalloc(void) { regcompiler(®.prov, PROVIDES, REG_EXTENDED|REG_ICASE); regcompiler(®.req_start, REQUIRED_START, REG_EXTENDED|REG_ICASE|REG_NEWLINE); regcompiler(®.req_stop, REQUIRED_STOP, REG_EXTENDED|REG_ICASE|REG_NEWLINE); regcompiler(®.shl_start, SHOULD_START, REG_EXTENDED|REG_ICASE|REG_NEWLINE); regcompiler(®.shl_stop, SHOULD_STOP, REG_EXTENDED|REG_ICASE|REG_NEWLINE); regcompiler(®.start_bf, START_BEFORE, REG_EXTENDED|REG_ICASE|REG_NEWLINE); regcompiler(®.stop_af, STOP_AFTER, REG_EXTENDED|REG_ICASE|REG_NEWLINE); regcompiler(®.def_start, DEFAULT_START, REG_EXTENDED|REG_ICASE|REG_NEWLINE); regcompiler(®.def_stop, DEFAULT_STOP, REG_EXTENDED|REG_ICASE|REG_NEWLINE); regcompiler(®.desc, DESCRIPTION, REG_EXTENDED|REG_ICASE|REG_NEWLINE); regcompiler(®.interact, INTERACTIVE, REG_EXTENDED|REG_ICASE|REG_NEWLINE); } static inline void scan_script_reset(void) attribute((always_inline)); static inline void scan_script_reset(void) { xreset(script_inf.provides); xreset(script_inf.required_start); xreset(script_inf.required_stop); xreset(script_inf.should_start); xreset(script_inf.should_stop); xreset(script_inf.start_before); xreset(script_inf.stop_after); xreset(script_inf.default_start); xreset(script_inf.default_stop); xreset(script_inf.description); xreset(script_inf.interactive); } /* * return name of upstart job if the script is a symlink to * /lib/init/upstart-job, or NULL if path do not point to an * upstart job. */ static char *is_upstart_job(const char *path) { uint deep = 0; char buf[PATH_MAX+1]; char *script = xstrdup(path); char *retval = basename(path); /* GNU basename */ buf[PATH_MAX] = '\0'; do { struct stat statbuf; int len; if (deep++ > MAXSYMLINKS) { errno = ELOOP; warn("Can not determine upstart job name for %s: %s\n", path, strerror(errno)); break; } if (lstat(script, &statbuf) < 0) { warn("Can not stat %s: %s\n", path, strerror(errno)); break; } if (!S_ISLNK(statbuf.st_mode)) break; if ((len = readlink(script, buf, sizeof(buf)-1)) < 0) break; buf[len] = '\0'; if (buf[0] != '/') { /* restore relative links */ const char *lastslash; if ((lastslash = strrchr(script, '/'))) { size_t dirlen = lastslash - script + 1; if (dirlen + len > PATH_MAX) len = PATH_MAX - dirlen; memmove(&buf[dirlen], &buf[0], len + 1); memcpy(&buf[0], script, dirlen); } } free(script); if (strcmp(buf, upstartjob_path) == 0) { info(2, "script '%s' is upstart job\n", retval); return strdup(retval); } script = xstrdup(buf); } while (1); free(script); return (char*)0; } #define FOUND_LSB_HEADER 0x01 #define FOUND_LSB_DEFAULT 0x02 #define FOUND_LSB_OVERRIDE 0x04 #define FOUND_LSB_UPSTART 0x08 static int o_flags = O_RDONLY; static uchar scan_lsb_headers(const int dfd, const char *restrict const path, const boolean cache, const boolean ignore) attribute((nonnull(2))); static uchar scan_lsb_headers(const int dfd, const char *restrict const path, const boolean cache, const boolean ignore) { regmatch_t subloc[SUBNUM_SHD+1], *val = &subloc[SUBNUM-1], *shl = &subloc[SUBNUM_SHD-1]; char *upstart_job = (char*)0; char *begin = (char*)0, *end = (char*)0; char *pbuf = buf; FILE *script; uchar ret = 0; int fd = -1; #define provides script_inf.provides #define required_start script_inf.required_start #define required_stop script_inf.required_stop #define should_start script_inf.should_start #define should_stop script_inf.should_stop #define start_before script_inf.start_before #define stop_after script_inf.stop_after #define default_start script_inf.default_start #define default_stop script_inf.default_stop #define description script_inf.description #define interactive script_inf.interactive info(2, "Loading %s\n", path); if (NULL != (upstart_job = is_upstart_job(path))) { char cmd[PATH_MAX]; int len; len = snprintf(cmd, sizeof(cmd), "%s %s lsb-header", upstartjob_path, upstart_job); if (len < 0 || sizeof(cmd) == len) error("snprintf: insufficient buffer for %s\n", path); if ((script = popen(cmd, "r")) == (FILE*)0) error("popen(%s): %s\n", path, strerror(errno)); ret |= FOUND_LSB_UPSTART; } else { if ((fd = xopen(dfd, path, o_flags)) < 0 || (script = fdopen(fd, "r")) == (FILE*)0) error("fopen(%s): %s\n", path, strerror(errno)); #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 (void)posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); #endif } #define COMMON_ARGS buf, SUBNUM, subloc, 0 #define COMMON_SHD_ARGS buf, SUBNUM_SHD, subloc, 0 while (fgets(buf, sizeof(buf), script)) { /* Skip scanning above from LSB magic start */ if (!begin) { if ( (begin = strstr(buf, "### BEGIN INIT INFO")) ) { /* Let the latest LSB header override the one found earlier */ scan_script_reset(); } continue; } if (!provides && regexecutor(®.prov, COMMON_ARGS) == true) { if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; provides = xstrdup(pbuf+val->rm_so); } else provides = empty; } if (!required_start && regexecutor(®.req_start, COMMON_ARGS) == true) { if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; required_start = xstrdup(pbuf+val->rm_so); } else required_start = empty; } if (!required_stop && regexecutor(®.req_stop, COMMON_ARGS) == true) { if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; required_stop = xstrdup(pbuf+val->rm_so); } else required_stop = empty; } if (!should_start && regexecutor(®.shl_start, COMMON_SHD_ARGS) == true) { if (shl->rm_so < shl->rm_eo) { *(pbuf+shl->rm_eo) = '\0'; should_start = xstrdup(pbuf+shl->rm_so); } else should_start = empty; } if (!should_stop && regexecutor(®.shl_stop, COMMON_SHD_ARGS) == true) { if (shl->rm_so < shl->rm_eo) { *(pbuf+shl->rm_eo) = '\0'; should_stop = xstrdup(pbuf+shl->rm_so); } else should_stop = empty; } if (!start_before && regexecutor(®.start_bf, COMMON_SHD_ARGS) == true) { if (shl->rm_so < shl->rm_eo) { *(pbuf+shl->rm_eo) = '\0'; start_before = xstrdup(pbuf+shl->rm_so); } else start_before = empty; } if (!stop_after && regexecutor(®.stop_af, COMMON_SHD_ARGS) == true) { if (shl->rm_so < shl->rm_eo) { *(pbuf+shl->rm_eo) = '\0'; stop_after = xstrdup(pbuf+shl->rm_so); } else stop_after = empty; } if (!default_start && regexecutor(®.def_start, COMMON_ARGS) == true) { if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; default_start = xstrdup(pbuf+val->rm_so); } else default_start = empty; } #ifndef SUSE if (!default_stop && regexecutor(®.def_stop, COMMON_ARGS) == true) { if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; default_stop = xstrdup(pbuf+val->rm_so); } else default_stop = empty; } #endif if (!description && regexecutor(®.desc, COMMON_ARGS) == true) { if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; description = xstrdup(pbuf+val->rm_so); } else description = empty; } if (!interactive && regexecutor(®.interact, COMMON_ARGS) == true) { if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; interactive = xstrdup(pbuf+val->rm_so); } else interactive = empty; } /* Skip scanning below from LSB magic end */ if ((end = strstr(buf, "### END INIT INFO"))) break; } #undef COMMON_ARGS #undef COMMON_SHD_ARGS if (upstart_job) { pclose(script); free(upstart_job); upstart_job = 0; } else { #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 if (cache) { off_t deep = ftello(script); (void)posix_fadvise(fd, 0, deep, POSIX_FADV_WILLNEED); (void)posix_fadvise(fd, deep, 0, POSIX_FADV_DONTNEED); } else (void)posix_fadvise(fd, 0, 0, POSIX_FADV_NOREUSE); #endif fclose(script); } if (begin && end) ret |= FOUND_LSB_HEADER; if (begin && !end) { char *name = basename(path); if (*name == 'S' || *name == 'K') name += 3; warn("Script %s is broken: missing end of LSB comment.\n", name); if (!ignore) error("exiting now!\n"); } if (begin && end && (!provides || (provides == empty) || #ifdef SUSE !required_start || !required_stop || !default_start #else /* not SUSE */ !required_start || !required_stop || !default_start || !default_stop #endif /* not SUSE */ )) { char *name = basename(path); if (*name == 'S' || *name == 'K') name += 3; warn("Script %s is broken: incomplete LSB comment.\n", name); if (!provides) warn("missing `Provides:' entry: please add.\n"); if (provides == empty) warn("missing valid name for `Provides:' please add.\n"); if (!required_start) warn("missing `Required-Start:' entry: please add even if empty.\n"); if (!required_stop) warn("missing `Required-Stop:' entry: please add even if empty.\n"); if (!default_start) warn("missing `Default-Start:' entry: please add even if empty.\n"); #ifndef SUSE if (!default_stop) warn("missing `Default-Stop:' entry: please add even if empty.\n"); #endif } #undef provides #undef required_start #undef required_stop #undef should_start #undef should_stop #undef start_before #undef stop_after #undef default_start #undef default_stop #undef description #undef interactive return ret; } /* * Follow symlinks, return the basename of the file pointed to by * symlinks or the basename of the current path if no symlink. */ static char * scriptname(int dfd, const char *restrict const path, char **restrict first) attribute((malloc,nonnull(2))); static char * scriptname(int dfd, const char *restrict const path, char **restrict first) { uint deep = 0; char linkbuf[PATH_MAX+1]; char *script = xstrdup(path); strncpy(linkbuf, script, sizeof(linkbuf)-1); linkbuf[PATH_MAX] = '\0'; do { struct stat st; int linklen; if (deep++ > MAXSYMLINKS) { errno = ELOOP; warn("Can not determine script name for %s: %s\n", path, strerror(errno)); break; } if (xlstat(dfd, script, &st) < 0) { warn("Can not stat %s: %s\n", script, strerror(errno)); break; } if (!S_ISLNK(st.st_mode)) break; if ((linklen = xreadlink(dfd, script, linkbuf, sizeof(linkbuf)-1)) < 0) break; linkbuf[linklen] = '\0'; if (linkbuf[0] != '/') { /* restore relative links */ const char *lastslash; if ((lastslash = strrchr(script, '/'))) { size_t dirname_len = lastslash - script + 1; if (dirname_len + linklen > PATH_MAX) linklen = PATH_MAX - dirname_len; memmove(&linkbuf[dirname_len], &linkbuf[0], linklen + 1); memcpy(&linkbuf[0], script, dirname_len); } } free(script); script = xstrdup(linkbuf); if (deep == 1 && first) *first = xstrdup(basename(linkbuf)); } while (1); free(script); script = xstrdup(basename(linkbuf)); return script; } static uchar load_overrides(const char *restrict const dir, const char *restrict const name, const boolean cache, const boolean ignore) attribute((nonnull(1,2))); static uchar load_overrides(const char *restrict const dir, const char *restrict const name, const boolean cache, const boolean ignore) { uchar ret = 0; char fullpath[PATH_MAX+1]; struct stat statbuf; int n; n = snprintf(&fullpath[0], sizeof(fullpath), "%s%s/%s", (root && !set_override) ? root : "", dir, name); if (n >= (int)sizeof(fullpath) || n < 0) error("snprintf(): %s\n", strerror(errno)); if (stat(fullpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) ret = scan_lsb_headers(-1, fullpath, cache, ignore); if (ret & FOUND_LSB_HEADER) ret |= FOUND_LSB_OVERRIDE; return ret; } static uchar scan_script_defaults(int dfd, const char *const restrict path, const char *const restrict override_path, char **restrict first, const boolean cache, const boolean ignore) attribute((nonnull(2,3))); static uchar scan_script_defaults(int dfd, const char *restrict const path, const char *restrict const override_path, char **restrict first, const boolean cache, const boolean ignore) { char * name = scriptname(dfd, path, first); uchar ret = 0; if (!name) return ret; /* Reset old results */ scan_script_reset(); #ifdef SUSE /* Common script ... */ if (!strcmp(name, "halt")) { ret |= (FOUND_LSB_HEADER|FOUND_LSB_DEFAULT); goto out; } /* ... and its link */ if (!strcmp(name, "reboot")) { ret |= (FOUND_LSB_HEADER|FOUND_LSB_DEFAULT); goto out; } /* Common script for single mode */ if (!strcmp(name, "single")) { ret |= (FOUND_LSB_HEADER|FOUND_LSB_DEFAULT); goto out; } #endif /* SUSE */ /* Replace with headers from the script itself */ ret |= scan_lsb_headers(dfd, path, cache, ignore); /* Do not override the upstarts defaults, if we allow this * we have to change name to the link name otherwise the * name is always "upstart-job" */ if (ret & FOUND_LSB_UPSTART) goto out; /* Load values if the override file exist */ if ((ret & FOUND_LSB_HEADER) == 0) ret |= load_overrides("/usr/share/insserv/overrides", name, cache, ignore); else ret |= FOUND_LSB_DEFAULT; /* * Allow host-specific overrides to replace the content in the * init.d scripts */ ret |= load_overrides(override_path, name, cache, ignore); out: free(name); return ret; } static inline void scan_script_regfree() attribute((always_inline)); static inline void scan_script_regfree() { regfree(®.prov); regfree(®.req_start); regfree(®.req_stop); regfree(®.shl_start); regfree(®.shl_stop); regfree(®.start_bf); regfree(®.stop_af); regfree(®.def_start); regfree(®.def_stop); regfree(®.desc); regfree(®.interact); } static struct { char *location; const ushort lvl; const ushort seek; const char key; } attribute((aligned(sizeof(char*)))) runlevel_locations[] = { #ifdef SUSE /* SuSE's SystemV link scheme */ {"rc0.d/", LVL_HALT, LVL_NORM, '0'}, {"rc1.d/", LVL_ONE, LVL_NORM, '1'}, /* runlevel 1 switch over to single user mode */ {"rc2.d/", LVL_TWO, LVL_NORM, '2'}, {"rc3.d/", LVL_THREE, LVL_NORM, '3'}, {"rc4.d/", LVL_FOUR, LVL_NORM, '4'}, {"rc5.d/", LVL_FIVE, LVL_NORM, '5'}, {"rc6.d/", LVL_REBOOT, LVL_NORM, '6'}, {"rcS.d/", LVL_SINGLE, LVL_NORM, 'S'}, /* runlevel S is for single user mode */ {"boot.d/", LVL_BOOT, LVL_BOOT, 'B'}, /* runlevel B is for system initialization */ #else /* not SUSE (actually, Debian) */ {"../rc0.d/", LVL_HALT, LVL_NORM, '0'}, {"../rc1.d/", LVL_ONE, LVL_NORM, '1'}, /* runlevel 1 switch over to single user mode */ {"../rc2.d/", LVL_TWO, LVL_NORM, '2'}, {"../rc3.d/", LVL_THREE, LVL_NORM, '3'}, {"../rc4.d/", LVL_FOUR, LVL_NORM, '4'}, {"../rc5.d/", LVL_FIVE, LVL_NORM, '5'}, {"../rc6.d/", LVL_REBOOT, LVL_NORM, '6'}, {"../rcS.d/", LVL_BOOT, LVL_BOOT, 'S'}, /* runlevel S is for system initialization */ /* On e.g. Debian there exist no boot.d */ #endif /* not SUSE */ }; #define RUNLEVLES (int)(sizeof(runlevel_locations)/sizeof(runlevel_locations[0])) int map_has_runlevels(void) { return RUNLEVLES; } char map_runlevel_to_key(const int runlevel) { if (runlevel >= RUNLEVLES) { warn("Wrong runlevel %d\n", runlevel); } return runlevel_locations[runlevel].key; } ushort map_key_to_lvl(const char key) { int runlevel; const char uckey = toupper(key); for (runlevel = 0; runlevel < RUNLEVLES; runlevel++) { if (uckey == runlevel_locations[runlevel].key) return runlevel_locations[runlevel].lvl; } warn("Wrong runlevel key '%c'\n", uckey); return 0; } const char *map_runlevel_to_location(const int runlevel) { if (runlevel >= RUNLEVLES) { warn("Wrong runlevel %d\n", runlevel); } return runlevel_locations[runlevel].location; } ushort map_runlevel_to_lvl(const int runlevel) { if (runlevel >= RUNLEVLES) { warn("Wrong runlevel %d\n", runlevel); } return runlevel_locations[runlevel].lvl; } ushort map_runlevel_to_seek(const int runlevel) { return runlevel_locations[runlevel].seek; } /* * Two helpers for runlevel bits and strings. */ ushort str2lvl(const char *restrict lvl) { char * token, *tmp = strdupa(lvl); ushort ret = 0; if (!tmp) error("%s", strerror(errno)); while ((token = strsep(&tmp, delimeter))) { if (!*token || strlen(token) != 1) continue; if (!strpbrk(token, "0123456sSbB")) continue; ret |= map_key_to_lvl(*token); } return ret; } char * lvl2str(const ushort lvl) { char * ptr, * last; char str[20]; int num; uint bit = 0x001; last = ptr = &str[0]; memset(ptr, '\0', sizeof(str)); for (num = 0; num < RUNLEVLES; num++) { if (bit & lvl) { if (ptr > last) *ptr++ = ' '; last = ptr; if (LVL_NORM & bit) *ptr++ = num + 48; #ifdef SUSE else if (LVL_SINGLE & bit) *ptr++ = 'S'; else if (LVL_BOOT & bit) *ptr++ = 'B'; #else /* not SUSE */ else if (LVL_BOOT & bit) *ptr++ = 'S'; #endif /* not SUSE */ else error("Wrong runlevel %d\n", num); } bit <<= 1; } if (strlen(str) == 0) return (char*)0; return xstrdup(str); } /* * Scan current service structure */ static void scan_script_locations(const char *const restrict path, const char *const restrict override_path, const boolean ignore) attribute((nonnull(1,2))); static void scan_script_locations(const char *const path, const char *const override_path, const boolean ignore) { int runlevel; pushd(path); for (runlevel = 0; runlevel < RUNLEVLES; runlevel++) { const char * rcd = (char*)0; struct stat st_script; struct dirent *d; DIR * rcdir; char * token; int dfd; rcd = map_runlevel_to_location(runlevel); rcdir = openrcdir(rcd); /* Creates runlevel directory if necessary */ if (rcdir == (DIR*)0) break; if ((dfd = dirfd(rcdir)) < 0) { closedir(rcdir); break; } pushd(rcd); while ((d = readdir(rcdir)) != (struct dirent*)0) { char * name = (char *)0; char * ptr = d->d_name; service_t * first; char * begin; /* Remember address of ptr handled by strsep() */ char order; uchar lsb; char type; if (*ptr != 'S' && *ptr != 'K') continue; type = *ptr; ptr++; if (strspn(ptr, "0123456789") < 2) continue; order = atoi(ptr); ptr += 2; if (xstat(dfd, d->d_name, &st_script) < 0) { xremove(dfd, d->d_name); /* dangling sym link */ continue; } lsb = scan_script_defaults(dfd, d->d_name, override_path, &name, true, ignore); if (!name) { warn("warning: script is corrupt or invalid: %s/%s%s\n", path, rcd, d->d_name); continue; } if (!script_inf.provides || script_inf.provides == empty) script_inf.provides = xstrdup(ptr); #ifndef SUSE if (!lsb) { script_inf.required_start = xstrdup(DEFAULT_DEPENDENCY); script_inf.required_stop = xstrdup(DEFAULT_DEPENDENCY); } #endif /* not SUSE */ first = (service_t*)0; begin = script_inf.provides; while ((token = strsep(&begin, delimeter)) && *token) { service_t * service; if (*token == '$') { warn("script %s provides system facility %s, skipped!\n", d->d_name, token); continue; } if (*token == '#') { warn("script %s provides facility %s with comment sign, skipped!\n", d->d_name, token); continue; } service = current_structure(token, order, runlevel, type); if (first) nickservice(first, service); else first = service; if (!makeprov(service, name)) continue; ++service->attr.ref; /* May enabled in several levels */ if (service->attr.flags & SERV_KNOWN) continue; service->attr.flags |= (SERV_KNOWN|SERV_ENABLED); if (!lsb) service->attr.flags |= SERV_NOTLSB; if ((lsb & FOUND_LSB_HEADER) == 0) { if ((lsb & (FOUND_LSB_DEFAULT|FOUND_LSB_OVERRIDE)) == 0) warn("warning: script '%s' missing LSB tags and overrides\n", d->d_name); else warn("warning: script '%s' missing LSB tags\n", d->d_name); } if (script_inf.required_start && script_inf.required_start != empty) { rememberreq(service, REQ_MUST, script_inf.required_start); #ifdef USE_COMPAT_EMPTY if (!script_inf.required_stop || script_inf.required_stop == empty) script_inf.required_stop = xstrdup(script_inf.required_start); #endif /* USE_COMPAT_EMPTY */ } if (script_inf.should_start && script_inf.should_start != empty) { rememberreq(service, REQ_SHLD, script_inf.should_start); #ifdef USE_COMPAT_EMPTY if (!script_inf.should_stop || script_inf.should_stop == empty) script_inf.should_stop = xstrdup(script_inf.should_start); #endif /* USE_COMPAT_EMPTY */ } if (script_inf.start_before && script_inf.start_before != empty) { reversereq(service, REQ_SHLD, script_inf.start_before); #ifdef USE_COMPAT_EMPTY if (!script_inf.stop_after || script_inf.stop_after == empty) script_inf.stop_after = xstrdup(script_inf.start_before); #endif /* USE_COMPAT_EMPTY */ } if (script_inf.required_stop && script_inf.required_stop != empty) { rememberreq(service, REQ_MUST|REQ_KILL, script_inf.required_stop); } if (script_inf.should_stop && script_inf.should_stop != empty) { rememberreq(service, REQ_SHLD|REQ_KILL, script_inf.should_stop); } if (script_inf.stop_after && script_inf.stop_after != empty) { reversereq(service, REQ_SHLD|REQ_KILL, script_inf.stop_after); } if (script_inf.interactive && 0 == strcmp(script_inf.interactive, "true")) { service->attr.flags |= SERV_INTRACT; } } if (name) xreset(name); scan_script_reset(); } /* while ((token = strsep(&begin, delimeter)) && *token) */ popd(); closedir(rcdir); } popd(); return; } /* * The /etc/insserv.conf scanning engine. */ static void scan_conf_file(const char *restrict file) attribute((nonnull(1))); static void scan_conf_file(const char *restrict file) { regmatch_t subloc[SUBCONFNUM], *val = (regmatch_t*)0; FILE *conf; info(2, "Loading %s\n", file); do { const char * fptr = file; if (*fptr == '/') fptr++; /* Try relativ location first */ if ((conf = fopen(fptr, "r"))) break; /* Try absolute location */ if ((conf = fopen(file, "r"))) break; goto err; } while (1); while (fgets(buf, sizeof(buf), conf)) { char *pbuf = &buf[0]; if (*pbuf == '#') continue; if (*pbuf == '\n') continue; if (regexecutor(&creg.isysfaci, buf, SUBCONFNUM, subloc, 0) == true) { char * virt = (char*)0, * real = (char*)0; val = &subloc[SUBCONF - 1]; if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; virt = pbuf+val->rm_so; } val = &subloc[SUBCONFNUM - 1]; if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; real = pbuf+val->rm_so; } if (virt) { list_t * ptr; boolean found = false; list_for_each(ptr, sysfaci_start) { if (!strcmp(getfaci(ptr)->name, virt)) { found = true; if(real) { list_t * r_list = &getfaci(ptr)->replace; char * token; while ((token = strsep(&real, delimeter))) { repl_t *restrict subst; string_t * r; if (posix_memalign((void*)&subst, sizeof(void*), alignof(repl_t)) != 0) error("%s", strerror(errno)); insert(&subst->r_list, r_list->prev); subst->flags = 0; r = &subst->r[0]; if (posix_memalign((void*)&r->ref, sizeof(void*), alignof(typeof(r->ref))+strsize(token)) != 0) error("%s", strerror(errno)); *r->ref = 1; r->name = ((char*)(r->ref))+alignof(typeof(r->ref)); strcpy(r->name, token); } } break; } } if (!found) { faci_t *restrict this; if (posix_memalign((void*)&this, sizeof(void*), alignof(faci_t)) != 0) error("%s", strerror(errno)); else { list_t * r_list = &this->replace; char * token; r_list->next = r_list; r_list->prev = r_list; insert(&this->list, sysfaci_start->prev); this->name = xstrdup(virt); while ((token = strsep(&real, delimeter))) { repl_t *restrict subst; string_t * r; if (posix_memalign((void*)&subst, sizeof(void*), alignof(repl_t)) != 0) error("%s", strerror(errno)); insert(&subst->r_list, r_list->prev); subst->flags = 0; r = &subst->r[0]; if (posix_memalign((void*)&r->ref, sizeof(void*), alignof(typeof(r->ref))+strsize(token)) != 0) error("%s", strerror(errno)); *r->ref = 1; r->name = ((char*)(r->ref))+alignof(typeof(r->ref)); strcpy(r->name, token); } } } } } if (regexecutor(&creg.isactive, buf, SUBCONFNUM, subloc, 0) == true) { char * key = (char*)0, * servs = (char*)0; val = &subloc[SUBCONF - 1]; if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; key = pbuf+val->rm_so; } val = &subloc[SUBCONFNUM - 1]; if (val->rm_so < val->rm_eo) { *(pbuf+val->rm_eo) = '\0'; servs = pbuf+val->rm_so; } if (key && *key == '<' && servs && *servs) { if (!strncmp("", key, strlen(key))) { char * token; while ((token = strsep(&servs, delimeter))) { service_t *service = addservice(token); service = getorig(service); service->attr.flags |= SERV_INTRACT; } } } } } fclose(conf); return; err: warn("fopen(%s): %s\n", file, strerror(errno)); } static int cfgfile_filter(const struct dirent *restrict d) attribute((nonnull(1))); static int cfgfile_filter(const struct dirent *restrict d) { boolean ret = false; const char * name = d->d_name; const char * end; if (*name == '.') goto out; if (!name || (*name == '\0')) goto out; if ((end = strrchr(name, '.'))) { end++; if (!strncmp(end, "rpm", 3) || /* .rpmorig, .rpmnew, .rmpsave, ... */ !strncmp(end, "ba", 2) || /* .bak, .backup, ... */ #ifdef SUSE !strcmp(end, "local") || /* .local are sourced by the basename */ #endif /* not SUSE */ !strcmp(end, "old") || !strcmp(end, "new") || !strcmp(end, "org") || !strcmp(end, "orig") || !strncmp(end, "dpkg", 3) || /* .dpkg-old, .dpkg-new ... */ !strcmp(end, "save") || !strcmp(end, "swp") || /* Used by vi like editors */ !strcmp(end, "core")) /* modern core dump */ { goto out; } } if ((end = strrchr(name, ','))) { end++; if (!strcmp(end, "v")) /* rcs-files */ goto out; } ret = true; out: return (int)ret; } static void scan_conf(const char *restrict file) attribute((nonnull(1))); static void scan_conf(const char *restrict file) { struct dirent** namelist = (struct dirent**)0; char path[PATH_MAX+1]; int n; regcompiler(&creg.isysfaci, CONFLINE, REG_EXTENDED|REG_ICASE); regcompiler(&creg.isactive, CONFLINE2, REG_EXTENDED|REG_ICASE); n = snprintf(&path[0], sizeof(path), "%s%s", (root && !set_insconf) ? root : "", file); if (n >= (int)sizeof(path) || n < 0) error("snprintf(): %s\n", strerror(errno)); scan_conf_file(path); n = snprintf(&path[0], sizeof(path), "%s%s.d", (root && !set_insconf) ? root : "", file); if (n >= (int)sizeof(path) || n < 0) error("snprintf(): %s\n", strerror(errno)); n = scandir(path, &namelist, cfgfile_filter, alphasort); if(n > 0) { while(n--) { struct stat st; char buf[PATH_MAX+1]; int r; r = snprintf(&buf[0], sizeof(buf), "%s/%s", path, namelist[n]->d_name); if (r >= (int)sizeof(buf) || r < 0) error("snprintf(): %s\n", strerror(errno)); if ((stat(buf, &st) < 0) || !S_ISREG(st.st_mode)) continue; scan_conf_file(buf); free(namelist[n]); } } if (namelist) free(namelist); regfree(&creg.isysfaci); regfree(&creg.isactive); } static void expand_faci(list_t *restrict rlist, list_t *restrict head, int *restrict deep) attribute((noinline,nonnull(1,2,3))); static void expand_faci(list_t *restrict rlist, list_t *restrict head, int *restrict deep) { repl_t * rent = getrepl(rlist); list_t * tmp, * safe, * ptr = (list_t*)0; list_for_each(tmp, sysfaci_start) { if (!strcmp(getfaci(tmp)->name, rent->r[0].name)) { ptr = &getfaci(tmp)->replace; break; } } if (!ptr || list_empty(ptr)) { delete(rlist); if (--(*rent->r[0].ref) <= 0) free(rent->r[0].ref); free(rent); goto out; } list_for_each_safe(tmp, safe, ptr) { repl_t * rnxt = getrepl(tmp); if (rnxt->flags & 0x0001) { error("Loop detected during expanding system facilities in the insserv.conf file(s): %s\n", rnxt->r[0].name); } if (*rnxt->r[0].name == '$') { if (*deep > 10) { warn("The nested level of the system facilities in the insserv.conf file(s) is to large\n"); goto out; } (*deep)++; rnxt->flags |= 0x0001; expand_faci(tmp, head, deep); rnxt->flags &= ~0x0001; (*deep)--; } else if (*deep > 0) { repl_t *restrict subst; if (posix_memalign((void*)&subst, sizeof(void*), alignof(repl_t)) != 0) error("%s", strerror(errno)); insert(&subst->r_list, head->prev); subst->r[0] = rnxt->r[0]; (*subst->r[0].ref) = 1; } } out: return; } static inline void expand_conf(void) { list_t *ptr; list_for_each(ptr, sysfaci_start) { list_t * rlist, * safe, * head = &getfaci(ptr)->replace; list_for_each_safe(rlist, safe, head) { repl_t * tmp = getrepl(rlist); if (*tmp->r[0].name == '$') { int deep = 0; tmp->flags |= 0x0001; expand_faci(rlist, rlist, &deep); tmp->flags &= ~0x0001; } } } } /* * Scan for a Start or Kill script within a runlevel directory. * We start were we leave the directory, the upper level * has to call rewinddir(3) if necessary. */ static inline char * scan_for(DIR *const restrict rcdir, const char *const restrict script, const char type) attribute((always_inline,nonnull(1,2))); static inline char * scan_for(DIR *const rcdir, const char *const script, const char type) { struct dirent *d; char * ret = (char*)0; while ((d = readdir(rcdir)) != (struct dirent*)0) { char * ptr = d->d_name; if (*ptr != type) continue; ptr++; if (strspn(ptr, "0123456789") < 2) continue; ptr += 2; if (!strcmp(ptr, script)) { ret = d->d_name; break; } } return ret; } #ifdef SUSE /* * A simple command line checker of the parent process to determine if this is * a sub process "/bin/sh" forked off for executing a temporary file for %preun, * %postun, %pre, or %post scriptlet. */ static inline boolean underrpm(void) { boolean ret = false; boolean mnt = true; const pid_t pp = getppid(); char buf[PATH_MAX], *argv[3], *ptr; # if defined(USE_RPMLIB) && (USE_RPMLIB > 0) char *tmppath, *shell; # endif /* USE_RPMLIB */ int argc, fd; ssize_t len; snprintf(buf, sizeof(buf)-1, "/proc/%lu/cmdline", (unsigned long)pp); do { if ((fd = open(buf, O_NOCTTY|O_RDONLY)) >= 0) break; if (!mnt || (errno != ENOENT)) goto out; if (mount("proc", "/proc", "proc", 0, NULL) < 0) error ("underrpm() can not mount /proc: %s\n", strerror(errno)); mnt = false; } while (1); memset(buf, '\0', sizeof(buf)); if ((len = read(fd , buf, sizeof(buf)-1)) < 0) goto out; ptr = &buf[0]; argc = 0; do { argv[argc++] = ptr; if (argc > 2) break; if ((len = len - (ssize_t)(ptr - &buf[0])) < 0) break; } while ((ptr = memchr(ptr, '\0', len)) && *(++ptr)); if (argc != 3) goto out; # if defined(USE_RPMLIB) && (USE_RPMLIB > 0) rpmReadConfigFiles(NULL, NULL); rpmFreeRpmrc(); if ((shell = rpmExpand("%_buildshell", NULL)) == NULL) shell = xstrdup("/bin/sh"); if (strncmp(argv[0], shell, strlen(shell)) != 0) { free(shell); goto out; } free(shell); if ((tmppath = rpmExpand("%_tmppath", NULL)) == NULL) tmppath = xstrdup("/var/tmp"); if (strncmp(argv[1], tmppath, strlen(tmppath)) != 0) { free(tmppath); goto out; } len = strlen(tmppath); free(tmppath); ptr = argv[1]; if (strncmp(ptr + len, "/rpm-tmp.", 9) != 0) goto out; # else /* not USE_RPMLIB */ if ((strcmp(argv[0], "/bin/sh") != 0) && (strcmp(argv[0], "/bin/bash") != 0)) goto out; if ((strncmp(argv[1], "/var/tmp/rpm-tmp.", 17) != 0) && (strncmp(argv[1], "/usr/tmp/rpm-tmp.", 17) != 0) && (strncmp(argv[1], "/tmp/rpm-tmp.", 13) != 0)) goto out; # endif /* not USE_RPMLIB */ if ((argc = atoi(argv[2])) >= 0 && argc <= 2) ret = true; out: if (fd >= 0) close(fd); if (!mnt) umount("/proc"); return ret; } #endif /* SUSE */ static struct option long_options[] = { {"verbose", 0, (int*)0, 'v'}, {"config", 1, (int*)0, 'c'}, {"dryrun", 0, (int*)0, 'n'}, {"default", 0, (int*)0, 'd'}, {"remove", 0, (int*)0, 'r'}, {"force", 0, (int*)0, 'f'}, {"path", 1, (int*)0, 'p'}, {"override",1, (int*)0, 'o'}, {"upstart-job",1, (int*)0, 'u'}, {"help", 0, (int*)0, 'h'}, { 0, 0, (int*)0, 0 }, }; static void help(const char *restrict const name) attribute((nonnull(1))); static void help(const char *restrict const name) { printf("Usage: %s [] [init_script|init_directory]\n", name); printf("Available options:\n"); printf(" -h, --help This help.\n"); printf(" -r, --remove Remove the listed scripts from all runlevels.\n"); printf(" -f, --force Ignore if a required service is missed.\n"); printf(" -v, --verbose Provide information on what is being done.\n"); printf(" -p , --path Path to replace " INITDIR ".\n"); printf(" -o , --override Path to replace " OVERRIDEDIR ".\n"); printf(" -c , --config Path to config file.\n"); printf(" -n, --dryrun Do not change the system, only talk about it.\n"); printf(" -d, --default Use default runlevels a defined in the scripts\n"); } /* * Do the job. */ int main (int argc, char *argv[]) { DIR * initdir; struct dirent *d; struct stat st_script; extension char * argr[argc]; char * path = INITDIR; char * override_path = OVERRIDEDIR; char * insconf = INSCONF; const char *const ipath = path; int runlevel, c, dfd; boolean del = false; boolean defaults = false; boolean ignore = false; boolean loadarg = false; myname = basename(*argv); #ifdef SUSE if (underrpm()) ignore = true; #endif /* SUSE */ if (getuid() == (uid_t)0) o_flags |= O_NOATIME; for (c = 0; c < argc; c++) argr[c] = (char*)0; while ((c = getopt_long(argc, argv, "c:dfrhvno:p:u:", long_options, (int *)0)) != -1) { size_t l; switch (c) { case 'c': if (optarg == (char*)0 || *optarg == '\0') goto err; insconf = optarg; set_insconf = true; break; case 'd': defaults = true; break; case 'r': del = true; break; case 'f': ignore = true; break; case 'v': verbose ++; break; case 'n': verbose ++; dryrun = true; break; case 'p': if (optarg == (char*)0 || *optarg == '\0') goto err; if (path != ipath) free(path); l = strlen(optarg) - 1; path = xstrdup(optarg); if (*(path+l) == '/') *(path+l) = '\0'; break; case 'o': if (optarg == (char*)0 || *optarg == '\0') goto err; override_path = optarg; set_override = true; break; case 'u': if (optarg == (char*)0 || *optarg == '\0') goto err; upstartjob_path = optarg; break; case '?': err: error("For help use: %s -h\n", myname); case 'h': help(myname); exit(0); default: break; } } argv += optind; argc -= optind; if (argc) loadarg = true; else if (del) error("usage: %s [[-r] init_script|init_directory]\n", myname); if (*argv) { char * token = strpbrk(*argv, delimeter); /* * Let us separate the script/service name from the additional arguments. */ if (token && *token) { *token = '\0'; *argr = ++token; } /* Catch `/path/script', `./script', and `path/script' */ if (strchr(*argv, '/')) { if (stat(*argv, &st_script) < 0) error("%s: %s\n", *argv, strerror(errno)); } else { pushd(path); if (stat(*argv, &st_script) < 0) error("%s: %s\n", *argv, strerror(errno)); popd(); } if (S_ISDIR(st_script.st_mode)) { const size_t l = strlen(*argv) - 1; if (path != ipath) free(path); path = xstrdup(*argv); if (*(path+l) == '/') *(path+l) = '\0'; argv++; argc--; if (argc || del) error("usage: %s [[-r] init_script|init_directory]\n", myname); } else { char * base, * ptr = xstrdup(*argv); if ((base = strrchr(ptr, '/'))) { if (path != ipath) free(path); *base = '\0'; path = ptr; } else free(ptr); } } if (strcmp(path, INITDIR) != 0) { char * tmp; if (*path != '/') { char * pwd = getcwd((char*)0, 0); size_t len = strlen(pwd)+1+strlen(path); root = (char*)malloc(len); if (!root) error("%s", strerror(errno)); strcpy(root, pwd); if (pwd[1]) strcat(root, "/"); strcat(root, path); free(pwd); } else root = xstrdup(path); if ((tmp = strstr(root, INITDIR))) { *tmp = '\0'; } else { free(root); root = (char*)0; } } c = argc; while (c--) { char * base; char * token = strpbrk(argv[c], delimeter); /* * Let us separate the script/service name from the additional arguments. */ if (token && *token) { *token = '\0'; argr[c] = ++token; } if (stat(argv[c], &st_script) < 0) { if (errno != ENOENT) error("%s: %s\n", argv[c], strerror(errno)); pushd(path); if (stat(argv[c], &st_script) < 0) error("%s: %s\n", argv[c], strerror(errno)); popd(); } if ((base = strrchr(argv[c], '/'))) { base++; argv[c] = base; } } #if defined(DEBUG) && (DEBUG > 0) for (c = 0; c < argc; c++) if (argr[c]) printf("Overwrite argument for %s is %s\n", argv[c], argr[c]); #endif /* DEBUG */ /* * Scan and set our configuration for virtual services. */ scan_conf(insconf); /* * Expand system facilities to real services */ expand_conf(); /* * Initialize the regular scanner for the scripts. */ scan_script_regalloc(); /* * Scan always for the runlevel links to see the current * link scheme of the services. */ scan_script_locations(path, override_path, ignore); /* * Clear out aliases found for scripts found up to this point. */ clear_all(); /* * Open the script directory */ if ((initdir = opendir(path)) == (DIR*)0 || (dfd = dirfd(initdir)) < 0) error("can not opendir(%s): %s\n", path, strerror(errno)); #if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 (void)posix_fadvise(dfd, 0, 0, POSIX_FADV_WILLNEED); (void)posix_fadvise(dfd, 0, 0, POSIX_FADV_SEQUENTIAL); #endif /* * Now scan for the service scripts and their LSB comments. */ pushd(path); /* * Scan scripts found in the command line to be able to resolve * all dependcies given within those scripts. */ if (argc > 1) for (c = 0; c < argc; c++) { const char *const name = argv[c]; service_t * first = (service_t*)0; char * provides, * begin, * token; const uchar lsb = scan_script_defaults(dfd, name, override_path, (char**)0, true, ignore); if ((lsb & FOUND_LSB_HEADER) == 0) { if ((lsb & (FOUND_LSB_DEFAULT|FOUND_LSB_OVERRIDE)) == 0) warn("warning: script '%s' missing LSB tags and overrides\n", name); else warn("warning: script '%s' missing LSB tags\n", name); } if (!script_inf.provides || script_inf.provides == empty) script_inf.provides = xstrdup(name); provides = xstrdup(script_inf.provides); begin = provides; while ((token = strsep(&begin, delimeter)) && *token) { service_t * service; if (*token == '$') { warn("script %s provides system facility %s, skipped!\n", name, token); continue; } if (*token == '#') { warn("script %s provides facility %s with comment sign, skipped!\n", name, token); continue; } service = addservice(token); if (first) nickservice(first, service); else first = service; service->attr.flags |= SERV_CMDLINE; } free(provides); } /* * Scan now all scripts found in the init.d/ directory */ for (;;) { service_t * service = (service_t*)0; char * token; char * begin = (char*)0; /* hold start pointer of strings handled by strsep() */ boolean hard = false; boolean isarg = false; uchar lsb = 0; #if defined(DEBUG) && (DEBUG > 0) int nobug = 0; #endif if ((d = readdir(initdir)) == (struct dirent*)0) { /* * If first script in argument list was loaded in advance, then * rewind the init.d/ directory stream and attempt to load all * other scripts. */ if (loadarg) { loadarg = false; rewinddir(initdir); continue; } break; } isarg = chkfor(d->d_name, argv, argc); /* * Load first script in argument list before all other scripts. This * avoids problems with loading scripts in underterministic sequence * returned by readdir(3). */ if (loadarg && !isarg) continue; if (loadarg && isarg && (curr_argc != 0)) continue; if (!loadarg && isarg && (curr_argc == 0)) continue; if (*d->d_name == '.') continue; errno = 0; /* d_type seems not to work, therefore use (l)stat(2) */ if (xlstat(dfd, d->d_name, &st_script) < 0) { warn("can not stat(%s)\n", d->d_name); continue; } if ((!S_ISREG(st_script.st_mode) && !S_ISLNK(st_script.st_mode)) || !(S_IXUSR & st_script.st_mode)) { if (S_ISDIR(st_script.st_mode)) continue; if (isarg) warn("script %s is not an executable regular file, skipped!\n", d->d_name); continue; } /* * Do extra sanity checking of symlinks in init.d/ dir, except if it * is named reboot, as that is a special case on SUSE */ if (S_ISLNK(st_script.st_mode) && ((strcmp(d->d_name, "reboot") != 0))) { char * base; char linkbuf[PATH_MAX+1]; int linklen; linklen = xreadlink(dfd, d->d_name, linkbuf, sizeof(linkbuf)-1); if (linklen < 0) continue; linkbuf[linklen] = '\0'; /* skip symbolic links to other scripts in this relative path */ if (!(base = strrchr(linkbuf, '/'))) { if (isarg) warn("script %s is a symlink to another script, skipped!\n", d->d_name); continue; } /* stat the symlink target and make sure it is a valid script */ if (xstat(dfd, d->d_name, &st_script) < 0) continue; if (!S_ISREG(st_script.st_mode) || !(S_IXUSR & st_script.st_mode)) { if (S_ISDIR(st_script.st_mode)) continue; if (isarg) warn("script %s is not an executable regular file, skipped!\n", d->d_name); continue; } } if (!strncmp(d->d_name, "README", strlen("README"))) { if (isarg) warn("script name %s is not valid, skipped!\n", d->d_name); continue; } if (!strncmp(d->d_name, "Makefile", strlen("Makefile"))) { if (isarg) warn("script name %s is not valid, skipped!\n", d->d_name); continue; } if (!strncmp(d->d_name, "core", strlen("core"))) { if (isarg) warn("script name %s is not valid, skipped!\n", d->d_name); continue; } /* Common scripts not used within runlevels */ if (!strcmp(d->d_name, "rx") || !strncmp(d->d_name, "skeleton", 8) || !strncmp(d->d_name, "powerfail", 9)) { if (isarg) warn("script name %s is not valid, skipped!\n", d->d_name); continue; } #ifdef SUSE if (!strcmp(d->d_name, "boot") || !strcmp(d->d_name, "rc")) #else /* not SUSE */ if (!strcmp(d->d_name, "rcS") || !strcmp(d->d_name, "rc")) #endif /* not SUSE */ { if (isarg) warn("script name %s is not valid, skipped!\n", d->d_name); continue; } if (cfgfile_filter(d) == 0) { if (isarg) warn("script name %s is not valid, skipped!\n", d->d_name); continue; } /* left by emacs like editors */ if (d->d_name[strlen(d->d_name)-1] == '~') { if (isarg) warn("script name %s is not valid, skipped!\n", d->d_name); continue; } if (strspn(d->d_name, "$.#%_+-\\*[]^:()~")) { if (isarg) warn("script name %s is not valid, skipped!\n", d->d_name); continue; } /* main scanner for LSB comment in current script */ lsb = scan_script_defaults(dfd, d->d_name, override_path, (char**)0, false, ignore); if ((lsb & FOUND_LSB_HEADER) == 0) { if ((lsb & (FOUND_LSB_DEFAULT|FOUND_LSB_OVERRIDE)) == 0) warn("warning: script '%s' missing LSB tags and overrides\n", d->d_name); else warn("warning: script '%s' missing LSB tags\n", d->d_name); } #ifdef SUSE /* Common script ... */ if (!strcmp(d->d_name, "halt")) { service_t *serv = addservice("halt"); serv = getorig(serv); makeprov(serv, d->d_name); runlevels(serv, 'S', "0"); serv->attr.flags |= (SERV_ALL|SERV_NOSTOP|SERV_INTRACT); continue; } /* ... and its link */ if (!strcmp(d->d_name, "reboot")) { service_t *serv = addservice("reboot"); serv = getorig(serv); makeprov(serv, d->d_name); runlevels(serv, 'S', "6"); serv->attr.flags |= (SERV_ALL|SERV_NOSTOP|SERV_INTRACT); continue; } /* Common script for single mode */ if (!strcmp(d->d_name, "single")) { service_t *serv = addservice("single"); serv = getorig(serv); makeprov(serv, d->d_name); runlevels(serv, 'S', "1 S"); serv->attr.flags |= (SERV_ALL|SERV_NOSTOP|SERV_INTRACT); rememberreq(serv, REQ_SHLD, "kbd"); continue; } #endif /* SUSE */ #ifndef SUSE if (!lsb) { script_inf.required_start = xstrdup(DEFAULT_DEPENDENCY); script_inf.required_stop = xstrdup(DEFAULT_DEPENDENCY); script_inf.default_start = xstrdup(DEFAULT_START_LVL); script_inf.default_stop = xstrdup(DEFAULT_STOP_LVL); } #endif /* not SUSE */ /* * Oops, no comment found, guess one */ if (!script_inf.provides || script_inf.provides == empty) { service_t * guess; script_inf.provides = xstrdup(d->d_name); /* * Use guessed service to find it within the the runlevels * (by using the list from the first scan for script locations). */ if ((guess = findservice(script_inf.provides))) { /* * Try to guess required services out from current scheme. * Note, this means that all services are required. */ if (!script_inf.required_start || script_inf.required_start == empty) { list_t * ptr; list_for_each_prev(ptr, s_start) { service_t * tmp = getservice(ptr); tmp = getorig(tmp); if (!tmp->attr.sorder) continue; if (tmp->attr.sorder >= guess->attr.sorder) continue; if (tmp->start->lvl & guess->start->lvl) { script_inf.required_start = xstrdup(tmp->name); break; } } } if (!script_inf.required_stop || script_inf.required_stop == empty) { list_t * ptr; list_for_each_prev(ptr, s_start) { service_t * tmp = getservice(ptr); tmp = getorig(tmp); if (!tmp->attr.korder) continue; if (tmp->attr.korder <= guess->attr.korder) continue; if (tmp->stopp->lvl & guess->stopp->lvl) { script_inf.required_stop = xstrdup(tmp->name); break; } } } if (!script_inf.default_start || script_inf.default_start == empty) { if (guess->start->lvl) script_inf.default_start = lvl2str(guess->start->lvl); } if (!script_inf.default_stop || script_inf.default_stop == empty) { if (guess->stopp->lvl) script_inf.default_stop = lvl2str(guess->stopp->lvl); } } else { /* !findservice(&guess, script_inf.provides) */ list_t * ptr; /* * Find out which levels this service may have out from current scheme. * Note, this means that the first requiring service wins. */ list_for_each(ptr, s_start) { service_t * cur; list_t * req; if (script_inf.default_start && script_inf.default_start != empty) break; cur = getservice(ptr); cur = getorig(cur); if (list_empty(&cur->sort.req) || !(cur->attr.flags & SERV_ENABLED)) continue; np_list_for_each(req, &cur->sort.req) { if (!strcmp(getreq(req)->serv->name, script_inf.provides)) { script_inf.default_start = lvl2str(getservice(ptr)->start->lvl); break; } } } list_for_each(ptr, s_start) { service_t * cur; list_t * rev; if (script_inf.default_stop && script_inf.default_stop != empty) break; cur = getservice(ptr); cur = getorig(cur); if (list_empty(&cur->sort.rev) || !(cur->attr.flags & SERV_ENABLED)) continue; np_list_for_each(rev, &cur->sort.rev) { if (!strcmp(getreq(rev)->serv->name, script_inf.provides)) { script_inf.default_stop = lvl2str(getservice(ptr)->stopp->lvl); break; } } } } /* !findservice(&guess, script_inf.provides) */ } /* * Use guessed service to find it within the the runlevels * (by using the list from the first scan for script locations). */ if (!service) { char * provides = xstrdup(script_inf.provides); service_t * first = (service_t*)0; begin = provides; while ((token = strsep(&begin, delimeter)) && *token) { if (*token == '$') { warn("script %s provides system facility %s, skipped!\n", d->d_name, token); continue; } if (*token == '#') { warn("script %s provides facility %s with comment sign, skipped!\n", d->d_name, token); continue; } service = addservice(token); if (first) nickservice(first, service); else first = service; #if defined(DEBUG) && (DEBUG > 0) nobug++; #endif if (!makeprov(service, d->d_name)) { if (!del || (del && !isarg)) warn("script %s: service %s already provided!\n", d->d_name, token); if (!del && !ignore && isarg) error("exiting now!\n"); if (!del || (del && !ignore && !isarg)) continue; /* Provide this service with an other name to be able to delete it */ service = addservice(d->d_name); service = getorig(service); service->attr.flags |= SERV_ALREADY; (void)makeprov(service, d->d_name); continue; } if (service) { boolean known = (service->attr.flags & SERV_KNOWN); service->attr.flags |= SERV_KNOWN; if (!known) { if (script_inf.required_start && script_inf.required_start != empty) { rememberreq(service, REQ_MUST, script_inf.required_start); #ifdef USE_COMPAT_EMPTY if (!script_inf.required_stop || script_inf.required_stop == empty) script_inf.required_stop = xstrdup(script_inf.required_start); #endif /* USE_COMPAT_EMPTY */ } if (script_inf.should_start && script_inf.should_start != empty) { rememberreq(service, REQ_SHLD, script_inf.should_start); #ifdef USE_COMPAT_EMPTY if (!script_inf.should_stop || script_inf.should_stop == empty) script_inf.should_stop = xstrdup(script_inf.should_start); #endif /* USE_COMPAT_EMPTY */ } if (script_inf.required_stop && script_inf.required_stop != empty) { rememberreq(service, REQ_MUST|REQ_KILL, script_inf.required_stop); } if (script_inf.should_stop && script_inf.should_stop != empty) { rememberreq(service, REQ_SHLD|REQ_KILL, script_inf.should_stop); } if (script_inf.interactive && 0 == strcmp(script_inf.interactive, "true")) { service->attr.flags |= SERV_INTRACT; } } if (script_inf.start_before && script_inf.start_before != empty) { reversereq(service, REQ_SHLD, script_inf.start_before); #ifdef USE_COMPAT_EMPTY if (!script_inf.stop_after || script_inf.stop_after == empty) script_inf.stop_after = xstrdup(script_inf.start_before); #endif /* USE_COMPAT_EMPTY */ } if (script_inf.stop_after && script_inf.stop_after != empty) { reversereq(service, REQ_SHLD|REQ_KILL, script_inf.stop_after); } /* * Use information from symbolic link structure to * check if all services are around for this script. */ if (isarg && !ignore) { boolean ok = true; if (del) ok = chkdependencies(service); else ok = chkrequired(service); if (!ok && !ignore) error("exiting now!\n"); } if (script_inf.default_start && script_inf.default_start != empty) { ushort deflvls = str2lvl(script_inf.default_start); if (service->attr.flags & SERV_ENABLED) { /* * Currently linked into service runlevel scheme, check * if the defaults are overwriten. Compare all bits, * which means `==' and not `&' and overwrite the defaults * of the current script. */ if (!defaults && (deflvls != service->start->lvl)) { if (!del && isarg && !(argr[curr_argc])) warn("warning: current start runlevel(s) (%s) of script `%s' overwrites defaults (%s).\n", service->start->lvl ? lvl2str(service->start->lvl) : "empty", d->d_name, lvl2str(deflvls)); } } else /* * Currently not linked into service runlevel scheme, info * needed for enabling interactive services at first time. */ service->start->lvl = deflvls; } else if (script_inf.default_start == empty) { if (service->attr.flags & SERV_ENABLED) { /* * Currently linked into service runlevel scheme, check * if the defaults are overwriten. Compare all bits, * which means `==' and not `&' and overwrite the defaults * of the current script. */ if (!defaults && service->start->lvl != 0) { warn("warning: current start runlevel(s) (%s) of script `%s' overwrites defaults (empty).\n", lvl2str(service->start->lvl), d->d_name); script_inf.default_start = lvl2str(service->start->lvl); } } } else if (!script_inf.default_start && (service->attr.flags & SERV_NOTLSB)) { #ifdef SUSE /* * Could be a none LSB script, use info from current link scheme. * If not found use default. */ if (service->attr.flags & SERV_ENABLED) script_inf.default_start = lvl2str(service->start->lvl); else script_inf.default_start = xstrdup(DEFAULT_START_LVL); #endif /* SUSE */ } #ifdef SUSE /* * This because SuSE boot script concept uses a differential link scheme. * Therefore default_stop is ignored and overwriten by default_start. */ xreset(script_inf.default_stop); if (script_inf.default_start && script_inf.default_start != empty) script_inf.default_stop = xstrdup(script_inf.default_start); else script_inf.default_stop = empty; oneway(script_inf.default_stop); #endif /* SUSE */ if (script_inf.default_stop && script_inf.default_stop != empty) { ushort deflvlk = str2lvl(script_inf.default_stop); /* * Compare all bits, which means `==' and not `&' and overwrite * the defaults of the current script. */ if (service->attr.flags & SERV_ENABLED) { /* * Currently linked into service runlevel scheme, check * if the defaults are overwriten. */ if (!defaults && (deflvlk != service->stopp->lvl)) { if (!del && isarg && !(argr[curr_argc])) warn("warning: current stop runlevel(s) (%s) of script `%s' overwrites defaults (%s).\n", service->stopp->lvl ? lvl2str(service->stopp->lvl) : "empty", d->d_name, lvl2str(deflvlk)); } } else /* * Currently not linked into service runlevel scheme, info * needed for enabling interactive services at first time. */ service->stopp->lvl = deflvlk; } else if (script_inf.default_stop == empty) { if (service->attr.flags & SERV_ENABLED) { /* * Currently linked into service runlevel scheme, check * if the defaults are overwriten. Compare all bits, * which means `==' and not `&' and overwrite the defaults * of the current script. */ if (!defaults && service->stopp->lvl != 0) { warn("warning: current stop runlevel(s) (%s) of script `%s' overwrites defaults (empty).\n", lvl2str(service->stopp->lvl), d->d_name); script_inf.default_stop = lvl2str(service->stopp->lvl); } } } else if (!script_inf.default_stop && (service->attr.flags & SERV_NOTLSB)) { #ifdef SUSE /* * Could be a none LSB script, use info from current link scheme. * If not found use default. */ if (service->attr.flags & SERV_ENABLED) script_inf.default_stop = lvl2str(service->stopp->lvl); else script_inf.default_stop = xstrdup(DEFAULT_STOP_LVL); #endif /* SUSE */ } } } free(provides); } #ifdef SUSE /* Ahh ... set default multiuser with network */ if (!script_inf.default_start || script_inf.default_start == empty) { if (!script_inf.default_start) warn("Default-Start undefined, assuming default start runlevel(s) for script `%s'\n", d->d_name); script_inf.default_start = xstrdup(DEFAULT_START_LVL); xreset(script_inf.default_stop); script_inf.default_stop = xstrdup(script_inf.default_start); oneway(script_inf.default_stop); } #else /* not SUSE */ if (!script_inf.default_start) { warn("Default-Start undefined, assuming empty start runlevel(s) for script `%s'\n", d->d_name); script_inf.default_start = empty; } #endif /* not SUSE */ #ifdef SUSE if (!script_inf.default_stop || script_inf.default_stop == empty) { if (script_inf.default_start && script_inf.default_start != empty) script_inf.default_stop = xstrdup(script_inf.default_start); else script_inf.default_stop = xstrdup(DEFAULT_STOP_LVL); oneway(script_inf.default_stop); } #else /* not SUSE */ if (!script_inf.default_stop) { warn("Default-Stop undefined, assuming empty stop runlevel(s) for script `%s'\n", d->d_name); script_inf.default_stop = empty; } #endif /* not SUSE */ if (isarg && !defaults && !del) { if (argr[curr_argc]) { char * ptr = argr[curr_argc]; struct _mark { const char * wrd; const boolean sk; char * order; char ** str; } mark[] = { {"start=", true, (char*)0, &script_inf.default_start}, {"stop=", false, (char*)0, &script_inf.default_stop }, #if 0 {"reqstart=", false, (char*)0, &script_inf.required_start}, {"reqstop=", false, (char*)0, &script_inf.required_stop }, #endif {(char*)0, false, (char*)0, (char**)0} }; for (c = 0; mark[c].wrd; c++) { char * order = strstr(ptr, mark[c].wrd); if (order) mark[c].order = order; } for (c = 0; mark[c].wrd; c++) if (mark[c].order) { *(mark[c].order) = '\0'; mark[c].order += strlen(mark[c].wrd); } for (c = 0; mark[c].wrd; c++) if (mark[c].order) { size_t len = strlen(mark[c].order); if (len > 0) { char * ptr = mark[c].order + len - 1; if (*ptr == ',') *ptr = '\0'; } if (ignore) { service_t *arg = findservice(getprovides(d->d_name)); arg = getorig(arg); if (mark[c].sk) arg->start->lvl = 0; else arg->stopp->lvl = 0; } xreset(*(mark[c].str)); *(mark[c].str) = xstrdup(mark[c].order); } hard = true; #ifdef SUSE /* * This because SuSE boot script concept uses a differential link scheme. * Therefore default_stop is ignored and overwriten by default_start. */ if (strcmp(script_inf.default_stop, script_inf.default_start) != 0) { xreset(script_inf.default_stop); script_inf.default_stop = xstrdup(script_inf.default_start); oneway(script_inf.default_stop); } #endif /* SUSE */ } } #if defined(DEBUG) && (DEBUG > 0) if (!nobug) { fprintf(stderr, "internal BUG at line %d with script %s\n", __LINE__, d->d_name); exit(1); } #endif begin = script_inf.provides; while ((token = strsep(&script_inf.provides, delimeter)) && *token) { if (*token == '$') continue; if (*token == '#') continue; if (!service) service = addservice(token); service = getorig(service); if ((service->attr.flags & SERV_ENABLED) && !hard) { if (del) continue; if (!defaults) continue; } if (script_inf.default_start && script_inf.default_start != empty) runlevels(service, 'S', script_inf.default_start); if (script_inf.default_stop && script_inf.default_stop != empty) runlevels(service, 'K', script_inf.default_stop); } script_inf.provides = begin; /* Remember if not LSB conform script */ if (!lsb && service) { service = getorig(service); service->attr.flags |= SERV_NOTLSB; } } /* Reset remaining pointers */ scan_script_reset(); /* * Free the regular scanner for the scripts. */ scan_script_regfree(); /* back */ popd(); closedir(initdir); /* * Clear out aliases found for all scripts. */ clear_all(); /* * Set virtual dependencies for already enabled none LSB scripts. */ nonlsb_script(); /* * Handle the `$all' scripts */ all_script(); /* * Now generate for all scripts the dependencies */ follow_all(); if (is_loop_detected() && !ignore) error("exiting now without changing boot order!\n"); /* * Be sure that interactive scripts are the only member of * a start group (for parallel start only). */ active_script(); /* * Sorry but we support only [KS][0-9][0-9] */ if (maxstart > MAX_DEEP || maxstop > MAX_DEEP) error("Maximum of %u in ordering reached\n", MAX_DEEP); #if defined(DEBUG) && (DEBUG > 0) printf("Maxorder %d/%d\n", maxstart, maxstop); show_all(); #else # ifdef SUSE /* SuSE's SystemV link scheme */ pushd(path); for (runlevel = 0; runlevel < RUNLEVLES; runlevel++) { const ushort lvl = map_runlevel_to_lvl(runlevel); char nlink[PATH_MAX+1], olink[PATH_MAX+1]; const char * rcd = (char*)0; const char * script; service_t *serv; DIR * rcdir; if ((rcd = map_runlevel_to_location(runlevel)) == (char*)0) continue; rcdir = openrcdir(rcd); /* Creates runlevel directory if necessary */ if (rcdir == (DIR*)0) break; if ((dfd = dirfd(rcdir)) < 0) { closedir(rcdir); break; } pushd(rcd); /* * See if we found scripts which should not be * included within this runlevel directory. */ while ((d = readdir(rcdir)) != (struct dirent*)0) { const char * ptr = d->d_name; char type; if (*ptr != 'S' && *ptr != 'K') continue; type = *ptr; ptr++; if (strspn(ptr, "0123456789") != 2) continue; ptr += 2; if (xstat(dfd, d->d_name, &st_script) < 0) xremove(dfd, d->d_name); /* dangling sym link */ if (notincluded(ptr, type, runlevel)) { serv = findservice(getprovides(ptr)); if (defaults) { xremove(dfd, d->d_name); if (serv && --serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; } else if (lvl & LVL_ONEWAY) { xremove(dfd, d->d_name); if (serv && --serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; } else if (del && ignore) { if (serv && (serv->attr.flags & SERV_ALREADY)) { xremove(dfd, d->d_name); if (--serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; } } } } /* * Seek for scripts which are included, link or * correct order number if necessary. */ script = (char*)0; while ((serv = listscripts(&script, 'X', lvl))) { const boolean this = chkfor(script, argv, argc); boolean found, slink; char * clink; if (*script == '$') /* Do not link in virtual dependencies */ continue; slink = false; if ((serv->start->lvl & lvl) == 0) goto stop; sprintf(olink, "../%s", script); sprintf(nlink, "S%.2d%s", serv->attr.sorder, script); found = false; rewinddir(rcdir); while ((clink = scan_for(rcdir, script, 'S'))) { found = true; if (strcmp(clink, nlink)) { xremove(dfd, clink); /* Wrong order, remove link */ if (--serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; if (!this) { xsymlink(dfd, olink, nlink); /* Not ours, but correct order */ if (++serv->attr.ref) serv->attr.flags |= SERV_ENABLED; } if (this && !del) { xsymlink(dfd, olink, nlink); /* Restore, with correct order */ if (++serv->attr.ref) serv->attr.flags |= SERV_ENABLED; } } else { if (del && this) { xremove(dfd, clink); /* Found it, remove link */ if (--serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; } } } if (this) { /* * If we haven't found it and we shouldn't delete it * we try to add it. */ if (!del && !found) { xsymlink(dfd, olink, nlink); if (++serv->attr.ref) serv->attr.flags |= SERV_ENABLED; found = true; } } /* Start links done, now do Kill links */ slink = found; stop: if ((serv->stopp->lvl & lvl) == 0) continue; sprintf(olink, "../%s", script); sprintf(nlink, "K%.2d%s", serv->attr.korder, script); found = false; rewinddir(rcdir); while ((clink = scan_for(rcdir, script, 'K'))) { found = true; if (strcmp(clink, nlink)) { xremove(dfd, clink); /* Wrong order, remove link */ if (--serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; if (!this) { xsymlink(dfd, olink, nlink); /* Not ours, but correct order */ if (++serv->attr.ref) serv->attr.flags |= SERV_ENABLED; } if (this && !del) { xsymlink(dfd, olink, nlink); /* Restore, with correct order */ if (++serv->attr.ref) serv->attr.flags |= SERV_ENABLED; } } else { if (del && this) { xremove(dfd, clink); /* Found it, remove link */ if (--serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; } } } if (this && slink) { /* * If we haven't found it and we shouldn't delete it * we try to add it. */ if (!del && !found) { xsymlink(dfd, olink, nlink); if (++serv->attr.ref) serv->attr.flags |= SERV_ENABLED; } } } popd(); closedir(rcdir); } # else /* not SUSE but Debian SystemV link scheme */ /* * Remark: At SuSE we use boot scripts for system initialization which * will be executed by /etc/init.d/boot (which is equal to rc.sysinit). * At system reboot or system halt the stop links of those boot scripts * will be executed by /etc/init.d/halt. Don't know how todo this for * a traditional standard SystemV link scheme. Maybe for such an * approach a new directory halt.d/ whould be an idea. */ pushd(path); for (runlevel = 0; runlevel < RUNLEVLES; runlevel++) { char nlink[PATH_MAX+1], olink[PATH_MAX+1]; const char * rcd = (char*)0; const char * script; service_t * serv; ushort lvl, seek; DIR * rcdir; if ((rcd = map_runlevel_to_location(runlevel)) == (char*)0) continue; lvl = map_runlevel_to_lvl(runlevel); seek = map_runlevel_to_seek(runlevel); rcdir = openrcdir(rcd); /* Creates runlevel directory if necessary */ if (rcdir == (DIR*)0) break; if ((dfd = dirfd(rcdir)) < 0) { closedir(rcdir); break; } pushd(rcd); /* * See if we found scripts which should not be * included within this runlevel directory. */ while ((d = readdir(rcdir)) != (struct dirent*)0) { const char * ptr = d->d_name; char type; if (*ptr != 'S' && *ptr != 'K') continue; type = *ptr; ptr++; if (strspn(ptr, "0123456789") != 2) continue; ptr += 2; if (xstat(dfd, d->d_name, &st_script) < 0) xremove(dfd, d->d_name); /* dangling sym link */ if (notincluded(ptr, type, runlevel)) { serv = findservice(getprovides(ptr)); if (defaults) { xremove(dfd, d->d_name); if (serv && --serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; # ifndef USE_KILL_IN_BOOT } else if (lvl & LVL_BOOT) { xremove(dfd, d->d_name); if (serv && --serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; # endif /* USE_KILL_IN_BOOT */ } else if (del && ignore) { if (serv && (serv->attr.flags & SERV_ALREADY)) xremove(dfd, d->d_name); if (--serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; } } } script = (char*)0; while ((serv = listscripts(&script, 'X', seek))) { const boolean this = chkfor(script, argv, argc); boolean found; char * clink; char mode; if (*script == '$') /* Do not link in virtual dependencies */ continue; sprintf(olink, "../init.d/%s", script); if (serv->stopp->lvl & lvl) { # ifndef USE_KILL_IN_BOOT if (lvl & LVL_BOOT) /* No kill links in rcS.d */ continue; # endif /* USE_KILL_IN_BOOT */ sprintf(nlink, "K%.2d%s", serv->attr.korder, script); mode = 'K'; } else if (serv->start->lvl & lvl) { sprintf(nlink, "S%.2d%s", serv->attr.sorder, script); mode = 'S'; } else continue; /* We aren't suppose to be on this runlevel */ found = false; rewinddir(rcdir); while ((clink = scan_for(rcdir, script, mode))) { found = true; if (strcmp(clink, nlink)) { xremove(dfd, clink); /* Wrong order, remove link */ if (--serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; if (!this) { xsymlink(dfd, olink, nlink); /* Not ours, but correct order */ if (++serv->attr.ref) serv->attr.flags |= SERV_ENABLED; } if (this && !del) { xsymlink(dfd, olink, nlink); /* Restore, with correct order */ if (++serv->attr.ref) serv->attr.flags |= SERV_ENABLED; } } else { if (del && this) { xremove(dfd, clink); /* Found it, remove link */ if (--serv->attr.ref <= 0) serv->attr.flags &= ~SERV_ENABLED; } } } if (this) { /* * If we haven't found it and we shouldn't delete it * we try to add it. */ if (!del && !found) { xsymlink(dfd, olink, nlink); if (++serv->attr.ref) serv->attr.flags |= SERV_ENABLED; found = true; } } } popd(); closedir(rcdir); } # endif /* !SUSE, standard SystemV link scheme */ #endif /* !DEBUG */ /* * Do the makedep */ makedep(); /* * Back to the root(s) */ popd(); /* * Make valgrind happy */ if (path != ipath) free(path); if (root) free(root); return 0; } insserv-1.14.0/listing.c0000644017777601777750000007212011263111260014517 0ustar nobodynobody/* * listing.c * * Copyright 2000-2009 Werner Fink, 2000 SuSE GmbH Nuernberg, Germany, * 2003 SuSE Linux AG, Germany. * 2007-2009 SuSE Linux Products GmbH Nuernberg, Germany * * This source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include "listing.h" int maxstart = 0; /* Maximum start order of runlevels 0 upto 6 and S */ int maxstop = 0; /* Maximum stop order of runlevels 0 upto 6 and S */ static int *maxorder; /* Pointer to one of above */ /* See listing.c for list_t and list_entry() macro */ #define getdir(list) list_entry((list), dir_t, d_list) #define getlink(list) list_entry((list), link_t, l_list) #define getnextlink(list) (list_empty(list) ? (dir_t*)0 : getlink((list)->next)->target) /* * We handle services (aka scripts) as directories because * dependencies can be handels as symbolic links therein. * A provided service will be linked into a required service. * For the general type of linked lists see listing.h. */ typedef struct dir_struct dir_t; typedef struct link_struct { list_t l_list; /* The linked list of symbolic links */ dir_t *restrict target; } __align link_t; /* This is a "symbolic link" */ typedef struct handle_struct { list_t link; /* The linked list of symbolic start/stop links in the directory */ level_t run; ushort flags; uchar mindeep; /* Default start/stop deep if any */ uchar deep; /* Current start/stop deep */ char * name; } __align handle_t; struct dir_struct { list_t d_list; /* The peg into linked list to other directories */ handle_t start; handle_t stopp; service_t *restrict serv; int ref; char * script; char * name; } __align; /* This is a "directory" */ #define attof(dir) (&(dir)->serv->attr) /* * The linked list off all directories, note that the s_list * entry within the dir_struct is used as the peg pointer. */ static list_t dirs = { &dirs, &dirs }; static list_t * d_start = &dirs; #define DIR_SCAN 0x0001 #define DIR_LOOP 0x0002 #define DIR_LOOPREPORT 0x0004 #define DIR_MAXDEEP 0x0008 /* * The linked list off all services, note that the d_list * entry within the service_struct is used as the peg pointer. */ static list_t servs = { &servs, &servs }; list_t * s_start = &servs; /* * Provide or find a service dir, set initial states and * link it into the maintaining if a new one. */ static inline dir_t * providedir(const char *restrict const name) attribute((malloc,always_inline,nonnull(1))); static inline dir_t * providedir(const char *restrict const name) { dir_t *restrict dir = (dir_t*)0; service_t *restrict serv; list_t * ptr; list_for_each_prev(ptr, d_start) { dir = getdir(ptr); if (!strcmp(dir->name, name)) goto out; } if (posix_memalign((void*)&serv, sizeof(void*), alignof(service_t)+strsize(name)) != 0) error("%s", strerror(errno)); memset(serv, 0, alignof(service_t)+strsize(name)); insert(&serv->s_list, s_start->prev); serv->name = ((char*)serv)+alignof(service_t); if (posix_memalign((void*)&dir, sizeof(void*), alignof(dir_t)) != 0) error("%s", strerror(errno)); memset(dir, 0, alignof(dir_t)); insert(&dir->d_list, d_start->prev); dir->ref = 1; serv->dir = (void*)dir; dir->serv = serv; initial(&dir->start.link); initial(&dir->stopp.link); initial(&serv->sort.req); initial(&serv->sort.rev); strcpy(serv->name, name); dir->name = serv->name; dir->start.name = serv->name; dir->stopp.name = serv->name; dir->start.mindeep = 1; dir->stopp.mindeep = 1; serv->start = &dir->start.run; serv->stopp = &dir->stopp.run; out: return dir; } /* * Find or add and initialize a service */ service_t * addservice(const char *restrict const serv) attribute((malloc,nonnull(1))); service_t * addservice(const char *restrict const serv) { service_t * this; list_t * ptr; dir_t * dir; list_for_each_prev(ptr, s_start) { this = getservice(ptr); if (!strcmp(this->name, serv)) goto out; } dir = providedir(serv); this = dir->serv; out: return this; } /* * Always return the address of the original service */ service_t * getorig(service_t *restrict const serv) { dir_t *const dir = (dir_t *)serv->dir; return dir->serv; } /* * Find a service dir by its script name. */ static inline dir_t * findscript(const char *restrict const script) attribute((always_inline,nonnull(1))); static inline dir_t * findscript(const char *restrict const script) { dir_t * ret = (dir_t*)0; list_t * ptr; list_for_each_prev(ptr, d_start) { dir_t * dir = getdir(ptr); if (!dir->script) continue; if (!strcmp(dir->script, script)) { ret = dir; break; } } return ret; } /* * Link the current service into the required service. * If the services do not exist, they will be created. */ static void ln_sf(dir_t *restrict cur, dir_t *restrict req, const char mode) attribute((nonnull(1,2))); static void ln_sf(dir_t *restrict cur, dir_t *restrict req, const char mode) { list_t * dent, * l_list = (mode == 'K') ? &req->stopp.link : &req->start.link; link_t *restrict this; if (cur == req) goto out; list_for_each_prev(dent, l_list) { dir_t * target = getlink(dent)->target; if (!strcmp(target->name, cur->name)) goto out; } if (posix_memalign((void*)&this, sizeof(void*), alignof(link_t)) == 0) { insert(&this->l_list, l_list->prev); this->target = cur; ++cur->ref; goto out; } error("%s", strerror(errno)); out: return; } /* * Remember loops to warn only once */ static inline boolean remembernode (handle_t *restrict const peg) attribute((always_inline,nonnull(1))); static inline boolean remembernode (handle_t *restrict const peg) { register boolean ret = true; if (peg->flags & DIR_LOOP) goto out; ret = false; peg->flags |= DIR_LOOP; out: return ret; } /* * Recursively called function to follow all * links within a service dir. * Just like a `find * -follow' within a directory tree * of depth one with cross linked dependencies. * * Be warned: the direction is naturally reversed. That * means that the most requested services have the lowest * order. In other word, an empty link list of a service * indicates that this service has a higher order number. */ #if defined(DEBUG) && (DEBUG > 0) # define loop_warn_two(a,b,o) \ warn("There is a loop between service %s and %s if %s (list:%d)\n", \ (a)->name, (b)->name, o, __LINE__) # define loop_warn_one(a,o) \ warn("There is a loop at service %s if %s (list:%d)\n", \ (a)->name, o, __LINE__) #else # define loop_warn_two(a,b,o) \ warn("There is a loop between service %s and %s if %s\n", (a)->name, (b)->name, o) # define loop_warn_one(a,o) \ warn("There is a loop at service %s if %s\n", (a)->name, o) #endif #define loop_check(a) \ ((a) && (a)->flags & DIR_LOOP) static void __follow (dir_t *restrict dir, dir_t *restrict skip, const int, const char, const char) #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) attribute((noinline,flatten,nonnull(1))); #else attribute((noinline,nonnull(1))); #endif static void __follow (dir_t *restrict dir, dir_t *restrict skip, const int level, const char mode, const char reportloop) { list_t * l_list; dir_t * tmp; register int deep = level; /* Link depth, maybe we're called recursively */ register int loop = 0; /* Count number of links in symbolic list */ handle_t * peg, * pskp = (handle_t*)0; const char * act; if (mode == 'K') { peg = &dir->stopp; if (skip) pskp = &skip->stopp; act = "stopped"; } else { peg = &dir->start; if (skip) pskp = &skip->start; act = "started"; } l_list = &peg->link; prefetch(l_list->next); if (peg->flags & DIR_SCAN) { if (pskp) { if (!remembernode(pskp) || !remembernode(peg)) loop_warn_two(peg, pskp, act); } else { /* Does this happen? */ if (!remembernode(pskp)) loop_warn_one(peg, act); } goto out; } if (deep < (peg->mindeep)) /* Default deep of this tree is higher */ deep = (peg->mindeep); if (deep > MAX_DEEP) { if ((peg->flags & DIR_MAXDEEP) == 0) warn("Max recursions depth %d for %s reached\n", MAX_DEEP, peg->name); peg->flags |= DIR_MAXDEEP; goto out; } for (tmp = dir; tmp; tmp = getnextlink(l_list)) { const typeof(attof(tmp)->flags) sflags = attof(tmp)->flags; register boolean recursion = true; handle_t * ptmp = (mode == 'K') ? &tmp->stopp : &tmp->start; uchar * order = &ptmp->deep; list_t * dent; if (loop++ > MAX_DEEP) { if (pskp) { if (!remembernode(pskp) || !remembernode(ptmp)) loop_warn_two(ptmp, pskp, act); } else { if (!remembernode(ptmp)) loop_warn_one(ptmp, act); } break; /* Loop detected, stop recursion */ } l_list = &ptmp->link; /* List of symbolic links for getnextlink() */ prefetch(l_list->next); if (!((peg->run.lvl) & (ptmp->run.lvl))) continue; /* Not same boot level */ if (pskp && pskp == ptmp) { if (!remembernode(pskp) || !remembernode(ptmp)) loop_warn_one(pskp, act); break; /* Loop detected, stop recursion */ } /* * As higher the link depth, as higher the start order. */ if (*order > deep) deep = *order; if (*order < deep) *order = deep; if ((ptmp->run.lvl) & LVL_ALL) { if (maxorder && (*maxorder < *order)) *maxorder = *order; } if (list_empty(l_list)) break; /* No further service requires this one */ /* * Do not count the dependcy deep of the system facilities * but follow them to count the replacing provides. */ if (*ptmp->name == '$') warn("System facilities not fully expanded, see %s!\n", dir->name); else if (++deep > MAX_DEEP) { if ((ptmp->flags & DIR_MAXDEEP) == 0) warn("Max recursions depth %d reached\n", MAX_DEEP); ptmp->flags |= DIR_MAXDEEP; break; } ptmp->flags |= DIR_SCAN; /* Mark this service for loop detection */ /* * If there are links in the links included, follow them */ np_list_for_each(dent, l_list) { dir_t * target = getlink(dent)->target; handle_t * ptrg = (mode == 'K') ? &target->stopp : &target->start; const typeof(attof(target)->flags) kflags = attof(target)->flags; if ((peg->run.lvl & ptrg->run.lvl) == 0) continue; /* Not same boot level */ if (target == tmp) break; /* Loop avoided */ if (target == dir) break; /* Loop avoided */ if (skip && skip == target) { if (!remembernode(pskp) || !remembernode(ptmp)) loop_warn_two(pskp, ptmp, act); recursion = false; break; /* Loop detected, stop recursion */ } if (mode == 'K') { if (kflags & SERV_FIRST) { warn("Stopping %s depends on %s and therefore on system facility `$all' which can not be true!\n", tmp->script ? tmp->script : tmp->name, target->script ? target->script : target->name); continue; } } else { if (sflags & SERV_ALL) { warn("Starting %s depends on %s and therefore on system facility `$all' which can not be true!\n", target->script ? target->script : target->name, tmp->script ? tmp->script : tmp->name); continue; } } if (ptrg->deep >= deep) /* Nothing new */ continue; /* The inner recursion */ __follow(target, tmp, deep, mode, reportloop); prefetch(dent->next); /* Just for the case an inner recursion was stopped */ if (loop_check(ptrg) || loop_check(ptmp) || loop_check(pskp)) { recursion = false; break; /* Loop detected, stop recursion */ } } ptmp->flags &= ~DIR_SCAN; /* Remove loop detection mark */ prefetch(l_list->next); if (!recursion) { if (reportloop && !(ptmp->flags & DIR_LOOPREPORT)) { warn(" loop involving service %s at depth %d\n", tmp->name, level); ptmp->flags |= DIR_LOOPREPORT; } break; /* Loop detected, stop recursion */ } } out: return; /* Make compiler happy */ } #undef loop_warn_two #undef loop_warn_one #undef loop_check /* * Helper for follow_all: start with depth one. */ static inline void follow(dir_t *restrict dir, const char mode, const char reportloop) attribute((always_inline,nonnull(1))); static inline void follow(dir_t *restrict dir, const char mode, const char reportloop) { const int deep = (mode == 'K') ? (dir->stopp.mindeep) : (dir->start.mindeep); /* Link depth starts here with one */ __follow(dir, (dir_t*)0, deep, mode, reportloop); } /* * Put not existing services into a guessed order. * The maximal order of not existing services can be * set if they are required by existing services. */ static void guess_order(dir_t *restrict dir, const char mode) attribute((nonnull(1))); static void guess_order(dir_t *restrict dir, const char mode) { handle_t * peg = (mode == 'K') ? &dir->stopp : &dir->start; list_t * l_list = &peg->link; register int min = 99; register int deep = 0; ushort lvl = 0; if (dir->script) /* Skip it because we have read it */ goto out; if (*dir->name == '$') { /* Don't touch our system facilities */ warn("System facilities not fully expanded, see %s!\n", dir->name); goto out; } /* No full loop required because we seek for the lowest order */ if (!list_empty(l_list)) { dir_t * target = getnextlink(l_list); handle_t * ptrg = (mode == 'K') ? &target->stopp : &target->start; uchar * order = &ptrg->deep; list_t * dent; if (min > *order) min = *order; lvl |= ptrg->run.lvl; list_for_each_prev(dent, l_list) { dir_t * tmp = getlink(dent)->target; handle_t * ptmp = (mode == 'K') ? &tmp->stopp : &tmp->start; uchar * order = &ptmp->deep; if (++deep > MAX_DEEP) break; if (target == dir) break; /* Loop detected */ if (min > *order) min = *order; lvl |= ptmp->run.lvl; } if (min > 1) { /* Set guessed order of this unknown script */ uchar * order = &peg->deep; *order = min - 1; peg->run.lvl |= lvl; /* Set guessed runlevels of this unknown script */ } else { peg->run.lvl = LVL_BOOT; } } out: return; } /* * Sort linked list of provides into start or stop order * during this set new start or stop order of the serives. */ #undef SORT_REQUESTS void lsort(const char type) { list_t sort = { &sort, &sort }; #ifdef SORT_REQUESTS list_t * this; #endif /* SORT_REQUESTS */ int order; switch (type) { case 'K': for (order = 0; order <= maxstop; order++) { list_t * ptr, * safe; list_for_each_safe(ptr, safe, d_start) { dir_t * dir = getdir(ptr); if (dir->stopp.deep == order) move_tail(ptr, &sort); } } join(&sort, d_start); #ifdef SORT_REQUESTS list_for_each(this, s_start) { service_t * serv = getservice(this); if (serv->attr.flags & SERV_DUPLET) continue; initial(&sort); for (order = 0; order <= maxstop; order++) { list_t * ptr, * safe; list_for_each_safe(ptr, safe, &serv->sort.rev) { req_t * rev = getreq(ptr); dir_t * dir = (dir_t*)rev->serv->dir; if (dir->stopp.deep == order) move_tail(ptr, &sort); } } join(&sort, &serv->sort.rev); } #endif /* SORT_REQUESTS */ break; default: for (order = 0; order <= maxstart; order++) { list_t * ptr, * safe; list_for_each_safe(ptr, safe, d_start) { dir_t * dir = getdir(ptr); if (dir->start.deep == order) move_tail(ptr, &sort); } } join(&sort, d_start); #ifdef SORT_REQUESTS list_for_each(this, s_start) { service_t * serv = getservice(this); if (serv->attr.flags & SERV_DUPLET) continue; initial(&sort); for (order = 0; order <= maxstart; order++) { list_t * ptr, * safe; list_for_each_safe(ptr, safe, &serv->sort.req) { req_t * req = getreq(ptr); dir_t * dir = (dir_t*)req->serv->dir; if (dir->start.deep == order) move_tail(ptr, &sort); } } join(&sort, &serv->sort.req); } #endif /* SORT_REQUESTS */ break; } } /* * Clear out aliases of existing services, that is that for *one* script there * exist several provides which could have have been required different by * other services. This avoids doing the same work several times. */ void nickservice(service_t *restrict orig, service_t *restrict nick) { dir_t * dir = (dir_t*)orig->dir; dir_t * cmp = (dir_t*)nick->dir; list_t * dent, * safe; if (dir == cmp) return; if (cmp->script && cmp->script != dir->script) return; list_for_each_safe(dent, safe, &cmp->start.link) { link_t * link = getlink(dent); dir_t * target = link->target; if (target == cmp) continue; ln_sf(target, dir, 'S'); /* remove the link from local link list but never free the target */ delete(dent); free(link); } list_for_each_safe(dent, safe, &cmp->stopp.link) { link_t * link = getlink(dent); dir_t * target = link->target; if (target == cmp) continue; ln_sf(target, dir, 'K'); /* remove the link from local link list but never free the target */ delete(dent); free(link); } delete(&cmp->d_list); /* remove alias entry from global service list */ /* remember levels of old start handle */ dir->start.run.lvl |= cmp->start.run.lvl; dir->start.flags |= cmp->start.flags; /* remember levels of old stop handle */ dir->stopp.run.lvl |= cmp->stopp.run.lvl; dir->stopp.flags |= cmp->stopp.flags; /* remember global flags of old provide */ orig->attr.flags |= nick->attr.flags; nick->attr.flags |= SERV_DUPLET; if (cmp->script && cmp->script != dir->script) { free(nick->attr.script); nick->attr.script = orig->attr.script; } nick->dir = (void*)dir; /* remember main provide */ nick->start = &dir->start.run; nick->stopp = &dir->stopp.run; if (--cmp->ref <= 0) free(cmp); list_for_each_safe(dent, safe, &nick->sort.req) { req_t * this = getreq(dent); boolean ok = true; list_t * req; list_for_each(req, &orig->sort.req) { if (!strcmp(this->serv->name,getreq(req)->serv->name)) { ok = false; break; } } if (!ok) { delete(dent); free(this); } else move_tail(dent, &orig->sort.req); } list_for_each_safe(dent, safe, &nick->sort.rev) { req_t * this = getreq(dent); boolean ok = true; list_t * rev; list_for_each(rev, &orig->sort.rev) { if (!strcmp(this->serv->name,getreq(rev)->serv->name)) { ok = false; break; } } if (!ok) { delete(dent); free(this); } else move_tail(dent, &orig->sort.rev); } } void clear_all(void) { list_t * this; /* * Find dangling links in global service list and remove them * if we by detect the remove bit from set above in the flags. */ list_for_each(this, d_start) { dir_t *dir = getdir(this); list_t *dent, *hold; list_for_each_safe(dent, hold, &dir->start.link) { link_t * link = getlink(dent); dir_t * target = link->target; if (target == dir) continue; if ((attof(target)->flags & SERV_DUPLET) == 0) continue; /* remove the link from local link list */ delete(dent); free(link); /* * Do not free allocated strings and structure if in use * never free cmp->attr.script as this remains always in use. */ if (--target->ref <= 0) free(target); } list_for_each_safe(dent, hold, &dir->stopp.link) { link_t * link = getlink(dent); dir_t * target = link->target; if (target == dir) continue; if ((attof(target)->flags & SERV_DUPLET) == 0) continue; /* remove the link from local link list */ delete(dent); free(link); /* * Do not free allocated strings and structure if in use * never free cmp->attr.script as this remains always in use. */ if (--target->ref <= 0) free(target); } } #if defined(DEBUG) && (DEBUG > 0) list_for_each(this, s_start) { service_t * srv = getservice(this); list_t * nxt, * hold; if (srv->attr.flags & SERV_DUPLET) continue; list_for_each_safe(nxt, hold, s_start) { list_t * dent, * safe; service_t * orv; orv = getservice(nxt); if ((orv->attr.flags & SERV_DUPLET) == 0) continue; if (srv->dir != orv->dir) continue; srv->attr.flags |= orv->attr.flags; srv->attr.flags &= ~SERV_DUPLET; list_for_each_safe(dent, safe, &orv->sort.req) { req_t * this = getreq(dent); boolean ok = true; list_t * req; list_for_each(req, &srv->sort.req) { if (!strcmp(this->serv->name,getreq(req)->serv->name)) { ok = false; break; } } if (!ok) { fprintf(stderr, "BUG: removed %s from start list of %s, missed getorig()?\n", this->serv->name, orv->name); delete(dent); free(this); } else { fprintf(stderr, "BUG: moved %s from start list of %s to %s, missed getorig()?\n", this->serv->name, orv->name, srv->name); move_tail(dent, &srv->sort.req); } } list_for_each_safe(dent, safe, &orv->sort.rev) { req_t * this = getreq(dent); boolean ok = true; list_t * rev; list_for_each(rev, &srv->sort.rev) { if (!strcmp(this->serv->name,getreq(rev)->serv->name)) { ok = false; break; } } if (!ok) { fprintf(stderr, "BUG: removed %s from start list of %s, missed getorig()?\n", this->serv->name, orv->name); delete(dent); free(this); } else { fprintf(stderr, "BUG: moved %s from start list of %s to %s, missed getorig()?\n", this->serv->name, orv->name, srv->name); move_tail(dent, &srv->sort.rev); } } } } #endif } /* * Follow all services and their dependencies recursivly. */ void follow_all(void) { list_t *tmp; /* * Follow all scripts and calculate the main ordering. */ list_for_each(tmp, d_start) { maxorder = &maxstart; follow(getdir(tmp), 'S', 1); maxorder = &maxstop; follow(getdir(tmp), 'K', 1); } /* * Guess order of not installed scripts in comparision * to the well known scripts. */ list_for_each(tmp, d_start) { maxorder = &maxstart; guess_order(getdir(tmp), 'S'); maxorder = &maxstop; guess_order(getdir(tmp), 'K'); } } boolean is_loop_detected(void) { list_t *tmp; list_for_each(tmp, d_start) { dir_t * dir = getdir(tmp); if (dir->start.flags & DIR_LOOPREPORT) return true; if (dir->stopp.flags & DIR_LOOPREPORT) return true; } return false; } /* * For debuging: show all services */ #if defined(DEBUG) && (DEBUG > 0) void show_all() { list_t *tmp; if (maxstop > 0) list_for_each(tmp, d_start) { char * script, *name, *lvlstr; dir_t * dir = getdir(tmp); handle_t * peg; uchar deep; ushort lvl; if (!dir) continue; name = dir->name; peg = &dir->stopp; lvl = peg->run.lvl; deep = peg->deep; if (attof(dir)->script) script = attof(dir)->script; else if (*name == '$') script = "%system"; else script = "%guessed"; lvlstr = lvl2str(lvl); info("K%.2d %s 0x%.2x '%s' (%s)\n", deep, name, lvl, lvlstr, script); xreset(lvlstr); } if (maxstart > 0) list_for_each(tmp, d_start) { char * script, *name, *lvlstr; dir_t * dir = getdir(tmp); handle_t * peg; uchar deep; ushort lvl; if (!dir) continue; name = dir->name; peg = &dir->start; lvl = peg->run.lvl; deep = peg->deep; if (attof(dir)->script) script = attof(dir)->script; else if (*name == '$') script = "%system"; else script = "%guessed"; lvlstr = lvl2str(lvl); info("S%.2d %s 0x%.2x '%s' (%s)\n", deep, name, lvl, lvlstr, script); xreset(lvlstr); } } #endif /* * Used within loops to get scripts not included in this runlevel */ boolean notincluded(const char *restrict const script, const char mode, const int runlevel) { list_t *tmp; boolean ret = false; const ushort lvl = map_runlevel_to_lvl (runlevel); list_for_each_prev(tmp, d_start) { dir_t * dir = getdir(tmp); level_t * run = (mode == 'K') ? &dir->stopp.run : &dir->start.run; if (run->lvl & lvl) /* Same runlevel */ continue; if (dir->script == (char*)0) /* No such file */ continue; if (strcmp(script, dir->script)) continue; /* Not this file */ ret = true; /* Not included */ break; } return ret; } /* * Used within loops to list services an for a given runlevel bit mask. */ service_t * listscripts(const char **restrict script, const char mode, const ushort lvl) { static list_t * tmp; service_t * serv; ushort level; dir_t * dir; if (!*script) tmp = d_start->next; do { serv = (service_t*)0; if (tmp == d_start) break; prefetch(tmp->next); dir = getdir(tmp); attof(dir)->korder = dir->stopp.deep; attof(dir)->sorder = dir->start.deep; serv = dir->serv; *script = serv->attr.script; switch (mode) { case 'X': level = (dir->stopp.run.lvl|dir->start.run.lvl); break; case 'K': level = dir->stopp.run.lvl; break; default: level = dir->start.run.lvl; break; } tmp = tmp->next; } while ((*script == (char*)0) || (level & lvl) == 0); return serv; } /* * THIS services DEPENDS on that service befor startup or shutdown. */ void requires(service_t *restrict this, service_t *restrict dep, const char mode) { ln_sf((dir_t*)this->dir, (dir_t*)dep->dir, mode); } /* * Set the runlevels of a service. */ void runlevels(service_t *restrict serv, const char mode, const char *restrict lvl) { dir_t * dir = (dir_t *)serv->dir; handle_t * peg = (mode == 'K') ? &dir->stopp : &dir->start; peg->run.lvl |= str2lvl(lvl); } /* * Reorder all services starting with a service * being in same runlevels. */ void setorder(const char *restrict script, const char mode, const int order, const boolean recursive) { dir_t * dir = findscript(script); handle_t * peg; list_t * tmp; if (!dir) goto out; if (mode == 'K') { peg = &dir->stopp; maxorder = &maxstop; } else { peg = &dir->start; maxorder = &maxstart; } if (peg->mindeep < order) peg->mindeep = order; /* Remember lowest default order deep */ if (peg->deep >= peg->mindeep) /* Nothing to do */ goto out; if (!recursive) { peg->deep = peg->mindeep; goto out; } /* * Follow the script and re-calculate the ordering. */ __follow(dir, (dir_t*)0, peg->mindeep, mode, 0); /* * Guess order of not installed scripts in comparision * to the well known scripts. */ list_for_each(tmp, d_start) guess_order(getdir(tmp), mode); out: return; } /* * Get the order of a script. */ int getorder(const char *restrict script, const char mode) { dir_t * dir = findscript(script); int order = 0; if (dir) { handle_t * peg = (mode == 'K') ? &dir->stopp : &dir->start; order = peg->deep; } return order; } /* * Provide a service if the corresponding script * was read and the scripts name was remembered. * A given script name marks a service as a readed one. * One script and several provided facilities leads * to the same order for those facilities. */ boolean makeprov(service_t *restrict serv, const char *restrict script) { dir_t *restrict alias = findscript(script); dir_t *restrict dir = (dir_t *restrict)serv->dir; boolean ret = true; if (!dir->script) { list_t * ptr; if (!alias) { serv->attr.script = xstrdup(script); serv->attr.flags |= SERV_SCRIPT; dir->script = serv->attr.script; } else dir->script = alias->script; list_for_each(ptr, s_start) { service_t * tmp = getservice(ptr); if (tmp == serv) continue; if (tmp->dir != serv->dir) continue; if (tmp->attr.script) continue; tmp->attr.script = dir->script; tmp->attr.flags |= SERV_SCRIPT; } } else if (strcmp(dir->script, script)) ret = false; return ret; } /* * Find the script name of a provided feature */ const char * getscript(const char *restrict prov) { char * script = (char*)0; list_t * ptr; list_for_each(ptr, s_start) { service_t * this = getservice(ptr); if (!strcmp(this->name, prov)) { if (this->attr.script) script = this->attr.script; break; } } return script; } /* * Return the provided service of a given script */ const char * getprovides(const char *restrict script) { const dir_t * dir = findscript(script); const char * prov = (const char*)0; if (dir) prov = dir->name; return prov; } /* * Find a specific service by its name */ service_t * findservice(const char *restrict const name) { list_t * ptr; service_t * ret = (service_t*)0; if (name == (const char*)0) goto out; list_for_each(ptr, s_start) { service_t * this = getservice(ptr); if (!strcmp(this->name, name)) { ret = this; break; } } out: return ret; } insserv-1.14.0/Makefile0000644017777601777750000001370711366260204014357 0ustar nobodynobody# # Makefile for compiling insserv tool # # Author: Werner Fink, # PACKAGE = insserv INITDIR = /etc/init.d INSCONF = /etc/insserv.conf #DESTDIR = /tmp/root #DEBUG = -DDEBUG=1 -Wpacked DEBUG = ISSUSE = -DSUSE DESTDIR = VERSION = 1.14.0 DATE = $(shell date +'%d%b%y' | tr '[:lower:]' '[:upper:]') # # Architecture # ifdef RPM_OPT_FLAGS COPTS = -g $(RPM_OPT_FLAGS) else ARCH = $(shell uname -i) ifeq ($(ARCH),i386) COPTS = -g -O3 -mcpu=i586 -mtune=i686 else COPTS = -g -O2 endif endif CFLAGS = -W -Wall $(COPTS) $(DEBUG) $(LOOPS) -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 \ $(ISSUSE) -DINITDIR=\"$(INITDIR)\" -DINSCONF=\"$(INSCONF)\" -pipe CLOOP = -falign-loops=0 LDFLAGS = -Wl,-O,3,--relax LIBS = ifdef USE_RPMLIB CFLAGS += -DUSE_RPMLIB=1 LDFLAGS += -Wl,--as-needed LIBS += -lrpm endif CC = gcc RM = rm -f MKDIR = mkdir -p RMDIR = rm -rf INSTBINFLAGS = -m 0755 INSTBIN = install $(INSTBINFLAGS) INSTSRPFLAGS = -m 0755 INSTSRP = install $(INSTSRPFLAGS) INSTDOCFLAGS = -c -m 0644 INSTDOC = install $(INSTDOCFLAGS) INSTCONFLAGS = -c -m 0644 INSTCON = install $(INSTCONFLAGS) LINK = ln -sf # SDOCDIR = $(DESTDIR)/usr/share/man/man8 SBINDIR = $(DESTDIR)/sbin CONFDIR = $(DESTDIR)/etc LSBDIR = $(DESTDIR)/lib/lsb USRLSBDIR = $(DESTDIR)/usr/lib/lsb # # Determine if a library provides a specific function # First argument is the function to test, the second # one is the library its self. # CTEST = $(CC) -nostdinc -fno-builtin -o /dev/null -xc cc-function = $(shell printf 'void *$(1)();\nint main(){return($(1)(0)?0:1);}'|$(CTEST) - -l$(2:lib%=%) > /dev/null 2>&1 && echo $(1)) # # The rules # TODO = insserv insserv.8 all: $(TODO) insserv: insserv.o listing.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) listing.o: listing.c listing.h config.h .system $(CC) $(CFLAGS) $(CLOOP) -c $< insserv.o: insserv.c listing.h config.h .system $(CC) $(CFLAGS) $(CLOOP) -c $< listing.h: .system config.h: ADRESSES = ^\#\s*if\s+defined\(HAS_[[:alnum:]_]+\)\s+&&\s+defined\(_ATFILE_SOURCE\) config.h: FUNCTIONS = $(shell sed -rn '/$(ADRESSES)/{ s/.*defined\(HAS_([[:alnum:]_]+)\).*/\1/p; }' listing.h) config.h: listing.h @echo '/* Generated automatically by running make -- do not edit */' > config.h @echo '#ifndef CONFIG_H' >> config.h @echo '#define CONFIG_H' >> config.h @for def in $(foreach func,$(FUNCTIONS),$(call cc-function,$(func),libc)); do \ echo "#define HAS_$$def"; \ done >> config.h @echo '#endif' >> config.h ifeq ($(ISSUSE),-DSUSE) insserv.8: insserv.8.in .system sed -r '\!@@(ELSE|NOT)_SUSE@@!,\!@@END_SUSE@@!d;\!@@(BEGIN|END)_SUSE@@!d' < $< > $@ else insserv.8: insserv.8.in .system sed -r '\!@@BEGIN_SUSE@@!,\!@@(ELSE|END)_SUSE@@!d;\!@@(NOT|END)_SUSE@@!d' < $< > $@ endif .system: SYSTEM=$(shell cat .system 2> /dev/null) .system: .force @test "$(SYSTEM)" = "$(ISSUSE)$(DEBUG)" || echo "$(ISSUSE)$(DEBUG)" > .system .force: .PHONY: clean clean: $(RM) *.o *~ $(TODO) config.h .depend.* .system ifneq ($(MAKECMDGOALS),clean) -include .depend.listing .depend.insserv .depend.listing:: listing.c listing.h @$(CC) $(CFLAGS) -M listing.c >$@ 2>/dev/null .depend.insserv:: insserv.c listing.h @$(CC) $(CFLAGS) -M insserv.c >$@ 2>/dev/null endif check: insserv ifeq ($(ISSUSE),-DSUSE) issuse=true tests/common # issuse=true tests/suse else tests/common endif install: $(TODO) check $(MKDIR) $(SBINDIR) $(MKDIR) $(SDOCDIR) $(MKDIR) $(CONFDIR) ifeq ($(ISSUSE),-DSUSE) $(MKDIR) $(LSBDIR) $(MKDIR) $(DESTDIR)/usr/lib $(MKDIR) $(USRLSBDIR) endif $(INSTBIN) insserv $(SBINDIR)/ $(INSTDOC) insserv.8 $(SDOCDIR)/ $(INSTCON) insserv.conf $(CONFDIR)/ ifeq ($(ISSUSE),-DSUSE) $(INSTCON) init-functions $(LSBDIR)/ $(INSTSRP) install_initd $(USRLSBDIR)/ $(INSTSRP) remove_initd $(USRLSBDIR)/ endif # # Make distribution # FILES = README \ COPYING \ CHANGES \ Makefile \ listing.c \ listing.h \ insserv.8.in \ insserv.c \ insserv.conf \ init-functions \ remove_initd \ install_initd \ tests/common \ tests/suite \ insserv-$(VERSION).lsm SVLOGIN=$(shell svn info | sed -rn '/Repository Root:/{ s|.*//(.*)\@.*|\1|p }') override TMP:=$(shell mktemp -d $(PACKAGE)-$(VERSION).XXXXXXXX) override TARBALL:=$(TMP)/$(PACKAGE)-$(VERSION).tar.bz2 override SFTPBATCH:=$(TMP)/$(VERSION)-sftpbatch override LSM=$(TMP)/$(PACKAGE)-$(VERSION).lsm $(LSM): $(TMP)/$(PACKAGE)-$(VERSION) @echo -e "Begin3\n\ Title: insserv tool for boot scripts\n\ Version: $(VERSION)\n\ Entered-date: $(DATE)\n\ Description: Used for enabling of installed boot scripts\n\ x by scanning comment headers which are LSB conform.\n\ Keywords: boot service control, LSB\n\ Author: Werner Fink \n\ Maintained-by: Werner Fink \n\ Primary-site: http://download.savannah.gnu.org/releases/sysvinit/\n\ x @UNKNOWN $(PACKAGE)-$(VERSION).tar.bz2\n\ Alternate-site: ftp.suse.com /pub/projects/init\n\ Platforms: Linux with System VR2 or higher boot scheme\n\ Copying-policy: GPL\n\ End" | sed 's@^ @@g;s@^x@@g' > $(LSM) dest: $(LSM) upload: $(SFTPBATCH) @sftp -b $< $(SVLOGIN)@dl.sv.nongnu.org:/releases/sysvinit mv $(TARBALL) $(LSM) . rm -rf $(TMP) $(SFTPBATCH): $(TARBALL).sig @echo progress > $@ @echo put $(TARBALL) >> $@ @echo chmod 644 $(notdir $(TARBALL)) >> $@ @echo put $(TARBALL).sig >> $@ @echo chmod 644 $(notdir $(TARBALL)).sig >> $@ @echo rm $(PACKAGE)-latest.tar.bz2 >> $@ @echo symlink $(notdir $(TARBALL)) $(PACKAGE)-latest.tar.bz2 >> $@ @echo quit >> $@ $(TARBALL).sig: $(TARBALL) @gpg -q -ba --use-agent -o $@ $< $(TARBALL): $(TMP)/$(PACKAGE)-$(VERSION) $(LSM) @tar --bzip2 --owner=nobody --group=nobody -cf $@ -C $(TMP) $(PACKAGE)-$(VERSION) @set -- `find $@ -printf '%s'` ; \ sed "s:@UNKNOWN:$$1:" < $(LSM) > $(LSM).tmp ; \ mv $(LSM).tmp $(LSM) $(TMP)/$(PACKAGE)-$(VERSION): .svn svn export . $@ @chmod -R a+r,u+w,og-w $@ @find $@ -type d | xargs -r chmod a+rx,u+w,og-w insserv-1.14.0/README0000644017777601777750000000116411260662111013565 0ustar nobodynobody Install ======= Edit the Makefile (set INITDIR to the path of the boot scripts used by the system during boot time), type `make' and `make install'. Downloading latest version ========================== The latest version of the insserv source is available from . Processes controlling ===================== This small package provides a tool for process controlling in System V boot scripts. The syntax is simple: insserv insserv etc/init.d insserv etc/init.d/myscript See manual page for more information. Happy booting, Werner Fink insserv-1.14.0/insserv.conf0000644017777601777750000000137411337506464015265 0ustar nobodynobody# # All local filesystems are mounted (done during boot phase) # $local_fs boot.localfs +boot.crypto # # Low level networking (ethernet card) # $network network # # Named is operational # $named +named +dnsmasq +lwresd $network # # All remote filesystems are mounted (note in some cases /usr may # be remote. Most applications that care will probably require # both $local_fs and $remote_fs) # $remote_fs $local_fs +nfs +smbfs # # System logger is operational # $syslog syslog # # SunRPC portmapper available # $portmap portmap # # The system time has been set correctly # $time boot.clock +xntpd # # Services which need to be interactive # apache apache2 boot.clock boot.crypto boot.crypto-early boot.localfs boot.rootfsck kbd kdump ntp insserv-1.14.0/install_initd0000755017777601777750000000004711260662111015467 0ustar nobodynobody#!/bin/sh exec /sbin/insserv ${1+"$@"}