Package: file-rc
Version: 0.8.12
Severity: important

Dear Maintainer,

In order to understand the order of treatment of runlevel.conf, I open the rc
script and found some errors:
 - sorting as brought by #327085 creates some errors, due to conflict on the
   "x" variable, also used in is_elem() function.
 - debug and verbose options are inverted between code and explanation

I corrected that, and use some time to improve the cosmetic:
 - complete reindentation
 - homogeneous use of "[]" and "test"
 - simplify execution conditions at the end of the file
 - remove "set centerline" which is useless

I hope this will be useful.

This also resolves #601985.

Please consider clarifying that to switch between one level and another one, rc
runs all kill commands and then all start commands, both in the "sort column"
order.

Thanks,
Benoit

*** End of the template - remove these lines ***


-- System Information:
Debian Release: wheezy/sid
  APT prefers testing
  APT policy: (990, 'testing'), (500, 'stable-updates'), (500, 'unstable'), 
(500, 'stable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 3.1.1-bf (PREEMPT)
Locale: LANG=fr_FR@euro, LC_CTYPE=fr_FR@euro (charmap=ISO-8859-15)
Shell: /bin/sh linked to /bin/bash

-- no debconf information
#! /bin/sh
# This is the file "rc" which starts and stops services for the different
# runlevels of the SysV init.
#
# Author: Winfried Trümper <wi...@xpilot.org>
# Misc fixes by Tom Lees <t...@lpsg.demon.co.uk>.
# Misc improvements and code rewrite by Martin Schulze <j...@debian.org>
# Misc improvements by Roland Rosenfeld <rol...@spinnaker.de>
# Partial rewrite by Alexander Wirt <formo...@debian.org>
# Bugfixes by Benoit Friry <ben...@friry.net>
#
#   Copyright (c) 1998  Martin Schulze <j...@debian.org>
#                 1998  Winfried Trümper <wi...@xpilot.org>
#                 1998  Miquel van Smoorenburg <miqu...@cistron.nl>
#            1999-2000  Roland Rosenfeld <rol...@spinnaker.de>
#                 2010  Alexander Wirt <formo...@formorer.de>
#                 2011  Benoit Friry <ben...@friry.net>
#
# Unlike traditional implementations it avoids the messy scheme with
# expressing the setup through links but reads a central config file
# instead. From a technical point of view both methods are almost
# equivalent.
#
# To be compatible with the common configuration scheme in the Linux-world,
# every script has two states: "on" or "off". The effect of this is that
# once it is switched on, it is never started again when the runlevel changes
# (it is only executed to switch it off if necessary).
#
# This scripts accept to different parameters, -v and -d. Please don't mix them
# -v means some verbose output during operations 
# -d means the same output, but also rc is doing a dry-run and does not try to 
#    start or stop anything. 
#
# The following section is taken from the original rc with slight
# modifications.

# Ignore CTRL-C only in this shell, so we can interrupt subprocesses.
trap ":" INT QUIT TSTP

# Set onlcr to avoid staircase effect.
stty onlcr 0>&1

verbose=0 
if [ "$1" = "-v" ]; then
	verbose=1
	shift
fi

debug=0
if [ "$1" = "-d" ]; then
	verbose=1
	debug=1
	shift
fi

# Is this done because RUNLEVEL and PREVLEVEL could be read-only?
#
# Now find out what the current and what the previous runlevel are.
runlevel=$RUNLEVEL
# Get first argument. Set new runlevel to this argument.
test "$1" != "" && runlevel="$1"
# Is this necessary?
#prevlevel=${PREVLEVEL:="N"}
prevlevel=$2
# Is this necessary?
export runlevel prevlevel

CFGFILE="/etc/runlevel.conf"
BAKCFG="/etc/runlevel.fallback"
LOCKFILE="/var/lock/runlevel.lock"

true=0
false=1

valid_min_seq=0
valid_max_seq=99

test $verbose -eq 1 && echo "rc: $prevlevel -> $runlevel"
  # wait for any lock to vanish (but only when not booting)
i=0
while [ -f "$LOCKFILE" ] && [ "$prevlevel" != "N" ]; do
	read pid < "$LOCKFILE"
	if ! kill -s 0 $pid > /dev/null 2>&1; then
		echo "$0: found stale lockfile '$LOCKFILE'. Ignoring it." >&2
		rm -f "$LOCKFILE" # external command (does not work on R/O fs)
		break
	fi
	if [ "$i" -gt "60" ]; then
		echo "Process no. '$pid' is locking the configuration database." >&2
		if [ "$runlevel" = "1" -o "$runlevel" = "6" ]; then
			# Try killing locking process, if booting, rebooting or halting.
			echo "Sending TERM signal to $pid." >&2
			kill -s 15 $pid
			sleep 5
			echo "Sending KILL signal to $pid." >&2	
			kill -s 9 $pid > /dev/null 2>&1
			rm -f "$LOCKFILE" # external command (does not work on R/O FS)
			sleep 5
			break
		else
			# Normal runlevel change (not boot, reboot or halt)
			echo "Terminating." >&2
			exit 1
		fi
	fi
	sleep 2
	echo -n "."
	i=$(($i + 1))
done


# This script is vital so we better keep an old copy of the configuration
# file as fallsave-configuration. This does not handle a broken config
# file, though.
if [ ! -f "$CFGFILE" ]; then
	echo "Missing configuration file '$CFGFILE' using fallback config."
	if [ ! -f "$BAKCFG" ]; then
		echo "No configuration file at all. You're in serious trouble now."
		echo "Please try to fix this problem with a root shell and reboot."
		if [ -f /etc/default/rcS ]; then
			# Read value of $CONSOLE:
			. /etc/default/rcS
		fi
		/sbin/sulogin $CONSOLE
		exit 1
	fi
	CFGFILE="$BAKCFG"
fi

is_valid_sequence() {
	if [ $# -ne 1 ]; then
		return $false
	fi

	case $1 in
		[0-9]|[0-9][0-9]) ;;
		*) return $false ;;
	esac

	if [ $1 -ge $valid_min_seq ] && [ $1 -le $valid_max_seq ]; then
		return $true
	fi
	return $false
}

element() {
	element="$1"
	case "$element" in
		reboot | R) element=0 ;;
		halt   | H) element=6 ;;
	esac
	
	test "$2" = "in" && shift
	list="$2"
	test "$list" = "-" && return 1
	test "$list" = "*" && return 0

	ORGIFS="$IFS"
	IFS=","
	set -- $list
	IFS="$ORGIFS"
	case $element in
		"$1" | "$2" | "$3" | "$4" | "$5" | "$6" | "$7" | "$8" | "$9")
		return 0
	esac
	return 1
}

is_elem() {
	elem=$1; shift

	for x in $*; do
		test "$x" = "$elem" && return 0
	done
	return 1
}

# Adds new levels to list of levels of the given command.  The entire
# list of commands and levels is tested.
#
pushlevel() {
	newcmd=$1;shift
	newlevels=$1; shift
	add="$newcmd:$newlevels"
	outline=""

	for i in $*; do
		cmd=${i%:*}
	if [ "$cmd" = "$newcmd" ]
	then
		outline="$outline$i,$newlevels "
		add=""
	else
		outline="$outline$i "
	fi
	done
	echo "$outline$add"
}


# CMDLIST ensures scripts are killed in reversed order
CMDLIST=""
STARTCMD=""
STOPCMD=""
# Experimental: To tell the scripts they are not called manually.
# (should be unset in init.d-scripts)
CALL_FROM_RC="yes"

test $verbose -eq 1 && echo "Reading configuration file $CFGFILE."

case $runlevel in
	0|6) start=stop; stop=stop;;
	*)   start=start; stop=stop;;
esac


# lock the configuration file
if [ "$prevlevel" != "N" ] && [ "$runlevel" != "1" ] && [ "$runlevel" != "6" ];
then
	(echo "$$" > "$LOCKFILE") || true
fi

while read  SORT_NO  OFF_LEVELS  ON_LEVELS  CMD  OPTIONS; do
	case "$SORT_NO" in
		\#*|""|\#) continue ;;
	esac
	test ! -f "$CMD" && continue
	is_valid_sequence "$SORT_NO" || continue

	# currently OPTIONS is completely ignored ... we _could_ pass them to the
	# init-script after "start" or "stop".

	test "$ON_LEVELS" != "-" && element "$runlevel" in "$ON_LEVELS" \
	&& STARTLIST=`pushlevel $SORT_NO:$CMD $ON_LEVELS $STARTLIST`

	if ! element "$prevlevel" in "$OFF_LEVELS"; then
		element "$runlevel" in "$OFF_LEVELS" \
			&& STOPLIST="$STOPLIST$SORT_NO:$CMD "
	fi

done < $CFGFILE

# remove lock of configuration file
if [ "$prevlevel" != "N" ] && [ "$runlevel" != "1" ] && [ "$runlevel" != "6" ];
then
	rm -f "$LOCKFILE"
fi


# First, run the KILL scripts.
for count_ten in 0 1 2 3 4 5 6 7 8 9; do
	for count_one in 0 1 2 3 4 5 6 7 8 9; do
		for script in $STOPLIST; do
			STOP_NUM=${script%%:*}
			if [ $STOP_NUM -eq $count_ten$count_one ]; then
				CMD=${script#*:}
				#test $verbose -eq 1 && echo "prev: $prevlevel"
				if [ "$prevlevel" != "N" ]; then
					case "$CMD" in
						*.sh)
							CMDLIST="$CMDLIST; (set -- $stop; . $CMD)"
							;;
						*)
							test -x "$CMD" && CMDLIST="$CMDLIST; $CMD $stop" 
							test $verbose -eq 1 && echo "Stop $CMD" 
							;;
					esac
				fi
			fi
		done
	done
done

# Then look at the start scripts
for count_ten in 0 1 2 3 4 5 6 7 8 9; do
	for count_one in 0 1 2 3 4 5 6 7 8 9; do
		for script in $STARTLIST; do
			START_NUM=${script%%:*}
			if [ $START_NUM -eq $count_ten$count_one ]; then
				comb=${script#*:}
				CMD=${comb%:*}
				if [ "$prevlevel" != "N" ]; then
					level=${comb#*:}
					if element "$prevlevel" in "$level" \
						&& ! is_elem $CMD $STOPLIST; then
						continue
					fi
				fi
				case "$CMD" in
					*.sh)
						CMDLIST="$CMDLIST; (set -- $start; . $CMD)"
						;;
					*)
						test -x "$CMD" && CMDLIST="$CMDLIST; $CMD $start"
						test $verbose -eq 1 && echo "Start $CMD"
						;;
					esac
			fi
		done
	done
done

# Execute the commands collected above
test $verbose -eq 1 && echo "$CMDLIST"
if [ $debug -eq 0 ]; then
	(trap - INT QUIT TSTP; sh -c "$CMDLIST")
fi

Reply via email to