mirror of
https://git.linuxfromscratch.org/lfs.git
synced 2025-08-05 11:05:20 +01:00
Presently, there are a lot of special cases: - runlevel 0 and 6 unconditionally run "script stop" if they find a Kxxscript symlink. This may lead to trying to stop an already stopped device if for example switching to runlevel 0/6 from runlevel 1. This can be fixed by stating the convention that it is the responsability of scripts to check that the service is running before killing it (or not running before starting it). Still, we shouldn't try to stop a service if it was marked K in the previous runlevel. And same for S files: we shouldn't try to start a service that was marked S in the previous runlevel. Note that changing runlevel is not a "reset": if a user has manually changed the state of a daemon, this state will remain the same upon changing runlevel if the S/K status of that dameon is the same in both runlevels. - Sxxscript symlinks in runlevel 0/6 are run as "script stop" instead of the more intuitive "script start". This does not interact well with LSB-tools (some scripts would need "Default-Start: S 0 6" but then it is impossible to get correct "Required-Start" or "Should-Start" fields). Furthermore, having a counter-intuitive behavior is error prone. So now runlevel 0/6 will run "script sart" for a Sxxscript.
228 lines
6.2 KiB
Bash
228 lines
6.2 KiB
Bash
#!/bin/bash
|
|
########################################################################
|
|
# Begin rc
|
|
#
|
|
# Description : Main Run Level Control Script
|
|
#
|
|
# Authors : Gerard Beekmans - gerard@linuxfromscratch.org
|
|
# : DJ Lucas - dj@linuxfromscratch.org
|
|
# Updates : Bruce Dubbs - bdubbs@linuxfromscratch.org
|
|
# : Pierre Labastie - pierre@linuxfromscratch.org
|
|
#
|
|
# Version : LFS 7.0
|
|
#
|
|
# Notes : Updates March 24th, 2022: new semantics of S/K files
|
|
# - Instead of testing that S scripts were K scripts in the
|
|
# previous runlevel, test that they were not S scripts
|
|
# - Instead of testing that K scripts were S scripts in the
|
|
# previous runlevel, test that they were not K scripts
|
|
# - S scripts in runlevel 0 or 6 are now run with
|
|
# "script start" (was "script stop" previously).
|
|
########################################################################
|
|
|
|
. /lib/lsb/init-functions
|
|
|
|
print_error_msg()
|
|
{
|
|
log_failure_msg
|
|
# $i is set when called
|
|
MSG="FAILURE:\n\nYou should not be reading this error message.\n\n"
|
|
MSG="${MSG}It means that an unforeseen error took place in\n"
|
|
MSG="${MSG}${i},\n"
|
|
MSG="${MSG}which exited with a return value of ${error_value}.\n"
|
|
|
|
MSG="${MSG}If you're able to track this error down to a bug in one of\n"
|
|
MSG="${MSG}the files provided by the ${DISTRO_MINI} book,\n"
|
|
MSG="${MSG}please be so kind to inform us at ${DISTRO_CONTACT}.\n"
|
|
log_failure_msg "${MSG}"
|
|
|
|
log_info_msg "Press Enter to continue..."
|
|
wait_for_user
|
|
}
|
|
|
|
check_script_status()
|
|
{
|
|
# $i is set when called
|
|
if [ ! -f ${i} ]; then
|
|
log_warning_msg "${i} is not a valid symlink."
|
|
SCRIPT_STAT="1"
|
|
fi
|
|
|
|
if [ ! -x ${i} ]; then
|
|
log_warning_msg "${i} is not executable, skipping."
|
|
SCRIPT_STAT="1"
|
|
fi
|
|
}
|
|
|
|
run()
|
|
{
|
|
if [ -z $interactive ]; then
|
|
${1} ${2}
|
|
return $?
|
|
fi
|
|
|
|
while true; do
|
|
read -p "Run ${1} ${2} (Yes/no/continue)? " -n 1 runit
|
|
echo
|
|
|
|
case ${runit} in
|
|
c | C)
|
|
interactive=""
|
|
${i} ${2}
|
|
ret=${?}
|
|
break;
|
|
;;
|
|
|
|
n | N)
|
|
return 0
|
|
;;
|
|
|
|
y | Y)
|
|
${i} ${2}
|
|
ret=${?}
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
return $ret
|
|
}
|
|
|
|
# Read any local settings/overrides
|
|
[ -r /etc/sysconfig/rc.site ] && source /etc/sysconfig/rc.site
|
|
|
|
DISTRO=${DISTRO:-"Linux From Scratch"}
|
|
DISTRO_CONTACT=${DISTRO_CONTACT:-"lfs-dev@lists.linuxfromscratch.org (Registration required)"}
|
|
DISTRO_MINI=${DISTRO_MINI:-"LFS"}
|
|
IPROMPT=${IPROMPT:-"no"}
|
|
|
|
# These 3 signals will not cause our script to exit
|
|
trap "" INT QUIT TSTP
|
|
|
|
[ "${1}" != "" ] && runlevel=${1}
|
|
|
|
if [ "${runlevel}" == "" ]; then
|
|
echo "Usage: ${0} <runlevel>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
previous=${PREVLEVEL}
|
|
[ "${previous}" == "" ] && previous=N
|
|
|
|
if [ ! -d /etc/rc.d/rc${runlevel}.d ]; then
|
|
log_info_msg "/etc/rc.d/rc${runlevel}.d does not exist.\n"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$runlevel" == "6" -o "$runlevel" == "0" ]; then IPROMPT="no"; fi
|
|
|
|
# Note: In ${LOGLEVEL:-7}, it is ':' 'dash' '7', not minus 7
|
|
if [ "$runlevel" == "S" ]; then
|
|
[ -r /etc/sysconfig/console ] && source /etc/sysconfig/console
|
|
dmesg -n "${LOGLEVEL:-7}"
|
|
fi
|
|
|
|
if [ "${IPROMPT}" == "yes" -a "${runlevel}" == "S" ]; then
|
|
# The total length of the distro welcome string, without escape codes
|
|
wlen=${wlen:-$(echo "Welcome to ${DISTRO}" | wc -c )}
|
|
welcome_message=${welcome_message:-"Welcome to ${INFO}${DISTRO}${NORMAL}"}
|
|
|
|
# The total length of the interactive string, without escape codes
|
|
ilen=${ilen:-$(echo "Press 'I' to enter interactive startup" | wc -c )}
|
|
i_message=${i_message:-"Press '${FAILURE}I${NORMAL}' to enter interactive startup"}
|
|
|
|
|
|
# dcol and icol are spaces before the message to center the message
|
|
# on screen. itime is the amount of wait time for the user to press a key
|
|
wcol=$(( ( ${COLUMNS} - ${wlen} ) / 2 ))
|
|
icol=$(( ( ${COLUMNS} - ${ilen} ) / 2 ))
|
|
itime=${itime:-"3"}
|
|
|
|
echo -e "\n\n"
|
|
echo -e "\\033[${wcol}G${welcome_message}"
|
|
echo -e "\\033[${icol}G${i_message}${NORMAL}"
|
|
echo ""
|
|
read -t "${itime}" -n 1 interactive 2>&1 > /dev/null
|
|
fi
|
|
|
|
# Make lower case
|
|
[ "${interactive}" == "I" ] && interactive="i"
|
|
[ "${interactive}" != "i" ] && interactive=""
|
|
|
|
# Read the state file if it exists from runlevel S
|
|
[ -r /run/interactive ] && source /run/interactive
|
|
|
|
# Stop all services marked as K, except if marked as K in the previous
|
|
# runlevel: it is the responsibility of the script to not try to kill
|
|
# a non running service
|
|
if [ "${previous}" != "N" ]; then
|
|
for i in $(ls -v /etc/rc.d/rc${runlevel}.d/K* 2> /dev/null)
|
|
do
|
|
check_script_status
|
|
if [ "${SCRIPT_STAT}" == "1" ]; then
|
|
SCRIPT_STAT="0"
|
|
continue
|
|
fi
|
|
|
|
suffix=${i#/etc/rc.d/rc${runlevel}.d/K[0-9][0-9]}
|
|
[ -e /etc/rc.d/rc${previous}.d/K[0-9][0-9]$suffix ] && continue
|
|
|
|
run ${i} stop
|
|
error_value=${?}
|
|
|
|
if [ "${error_value}" != "0" ]; then print_error_msg; fi
|
|
done
|
|
fi
|
|
|
|
if [ "${previous}" == "N" ]; then export IN_BOOT=1; fi
|
|
|
|
if [ "$runlevel" == "6" -a -n "${FASTBOOT}" ]; then
|
|
touch /fastboot
|
|
fi
|
|
|
|
|
|
# Start all services marked as S in this runlevel, except if marked as
|
|
# S in the previous runlevel
|
|
# it is the responsabily of the script to not try to start an already running
|
|
# service
|
|
for i in $( ls -v /etc/rc.d/rc${runlevel}.d/S* 2> /dev/null)
|
|
do
|
|
|
|
if [ "${previous}" != "N" ]; then
|
|
suffix=${i#/etc/rc.d/rc${runlevel}.d/S[0-9][0-9]}
|
|
[ -e /etc/rc.d/rc${previous}.d/S[0-9][0-9]$suffix ] && continue
|
|
fi
|
|
|
|
check_script_status
|
|
if [ "${SCRIPT_STAT}" == "1" ]; then
|
|
SCRIPT_STAT="0"
|
|
continue
|
|
fi
|
|
|
|
run ${i} start
|
|
|
|
error_value=${?}
|
|
|
|
if [ "${error_value}" != "0" ]; then print_error_msg; fi
|
|
done
|
|
|
|
# Store interactive variable on switch from runlevel S and remove if not
|
|
if [ "${runlevel}" == "S" -a "${interactive}" == "i" ]; then
|
|
echo "interactive=\"i\"" > /run/interactive
|
|
else
|
|
rm -f /run/interactive 2> /dev/null
|
|
fi
|
|
|
|
# Copy the boot log on initial boot only
|
|
if [ "${previous}" == "N" -a "${runlevel}" != "S" ]; then
|
|
cat $BOOTLOG >> /var/log/boot.log
|
|
|
|
# Mark the end of boot
|
|
echo "--------" >> /var/log/boot.log
|
|
|
|
# Remove the temporary file
|
|
rm -f $BOOTLOG 2> /dev/null
|
|
fi
|
|
|
|
# End rc
|