#!/bin/bash
# Copyright 1999-2005 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header$

trap ":" INT QUIT TSTP
source /sbin/functions.sh
umask 022

# Check that $1 exists ...
check_statedir() {
	[ -z "$1" ] && return 0

	if [ ! -d "$1" ] ; then
		if ! mkdir -p "$1" &>/dev/null ; then
			echo
			eerror "For Gentoo to function properly, \"$1\" needs to exist."
			/sbin/halt -f
		fi
	fi

	return 0
}

get_critical_services() {
	local x=
	CRITICAL_SERVICES=
	
	if [ -f "/etc/runlevels/${BOOTLEVEL}/.critical" ]
	then
		for x in $(< /etc/runlevels/${BOOTLEVEL}/.critical)
		do
			CRITICAL_SERVICES="${CRITICAL_SERVICES} ${x##*/}"
		done
	else
		CRITICAL_SERVICES="hostname"
	fi

	export CRITICAL_SERVICES

	return 0
}

# Save $1
argv1="$1"

# First time boot stuff goes here.  Note that 'sysinit' is an internal runlevel
# used to bring up local filesystems, and should not be started with /sbin/rc
# directly ...
if [ "${argv1}" = "sysinit" ]
then
	# Setup initial $PATH just in case
	PATH="/bin:/sbin:/usr/bin:/usr/sbin:${PATH}"

	echo
	echo -e "${GOOD}Gentoo Linux${GENTOO_VERS}; ${BRACKET}http://www.gentoo.org/${NORMAL}"
	echo -e " Copyright 1999-2005 Gentoo Foundation; Distributed under the GPLv2"
	echo

	check_statedir /proc
	check_statedir /dev

	# OpenVZ needs to mount proc from inside
	if [[ ! -e /proc/self ]]; then
		mount -t proc none /proc
	fi

	# We set the forced softlevel from the kernel command line
	# It needs to be run right after proc is mounted for the
	# boot runlevel
	setup_defaultlevels

	# $BOOT can be used by rc-scripts to test if it is the first time
	# the 'boot' runlevel is executed.  Now also needed by some stuff in
	# the 'sysinit' runlevel ...
	export BOOT="yes"

	start_critical_service() {
		(
		local retval=
		local service=$1
		# Needed for some addons like dm-crypt that starts in critical services
		local myservice=$1

		source "/etc/init.d/${service}" || eerror "Failed to source /etc/init.d/${service}"
		retval=$?
		[ "${retval}" -ne 0 ] && return "${retval}"
		[ -e "/etc/conf.d/${service}" ] && source "/etc/conf.d/${service}"
		source /etc/rc.conf

		start || eerror "Failed to start /etc/init.d/${service}"
		retval=$?

		return "${retval}"
		)
	}

	# We first try to find a locally defined list of critical services
	# for a particular runlevel.  If we cannot find it, we use the
	# defaults.
	get_critical_services

	# We do not want to break compatibility, so we do not fully integrate
	# these into /sbin/rc, but rather start them by hand ...
	for x in ${CRITICAL_SERVICES}
	do
		if ! start_critical_service "${x}"
		then
			echo
			eerror "One of more critical startup scripts failed to start!"
			eerror "Please correct this, and reboot ..."
			/sbin/halt -f
		fi
	done

	# Check that $svcdir exists ...
	check_statedir "${svcdir}"

	# Clear $svcdir from stale entries, but leave the caches around, as it
	# should help speed things up a bit
	rm -rf $(ls -d1 "${svcdir}/"* 2>/dev/null | \
	         grep -ve '\(depcache\|deptree\|envcache\)')

	# Update the dependency cache
	/sbin/depscan.sh -u

	# Now that the dependency cache are up to date, make sure these
	# are marked as started ...
	(
		# Needed for mark_service_started()
		source "${svclib}/sh/rc-services.sh"
		
		for x in ${CRITICAL_SERVICES}
		do
			mark_service_started "${x}"
		done
	)

	# Setup login records ... this has to be done here because when 
	# we exit this runlevel, init will write a boot record to utmp
	# If /var/run is readonly, then print a warning, not errors
	if touch /var/run/utmp 2>/dev/null
	then
		> /var/run/utmp
		touch /var/log/wtmp
		chgrp utmp /var/run/utmp /var/log/wtmp
		chmod 0664 /var/run/utmp /var/log/wtmp
		# Remove /var/run/utmpx (bug from the past)
		rm -f /var/run/utmpx
	else
		ewarn "Skipping /var/run/utmp initialization (ro root?)"
	fi

	exit 0
fi # Sysinit ends here

if [ "${argv1}" = "boot" ]
then
	setup_defaultlevels

	# $BOOT can be used by rc-scripts to test if it is the first time
	# the 'boot' runlevel is executed
	export BOOT="yes"

	# We reset argv1 to the bootlevel given on the kernel command line
	# if there is one
	argv1="${BOOTLEVEL}"
fi

source "${svclib}/sh/rc-services.sh"
source "${svclib}/sh/rc-daemon.sh"

if [ -f "${svcdir}/softlevel" ]
then
	# Set OLDSOFTLEVEL if we had a valid SOFTLEVEL
	export OLDSOFTLEVEL="$(< ${svcdir}/softlevel)"
else
	export OLDSOFTLEVEL=
fi
	
if [ -z "${argv1}" ]
then
	if [ -f "${svcdir}/softlevel" ]
	then
		export SOFTLEVEL="$(< ${svcdir}/softlevel)"
	else
		export SOFTLEVEL="${BOOTLEVEL}"
	fi
else
	export SOFTLEVEL="${argv1}"
fi

if [ ! -f "${svcdir}/softlevel" ]
then
	echo "${SOFTLEVEL}" > "${svcdir}/softlevel"
fi

# For keeping a list of services that fails during boot/halt
if [ ! -d "${svcdir}/failed" ]
then
	mkdir -p -m 0755 "${svcdir}/failed"
else
	rm -rf "${svcdir}"/failed/*
fi

if [ "${SOFTLEVEL}" = "reboot" -o "${SOFTLEVEL}" = "shutdown" ]
then
	myscripts=

elif [ ! -d "/etc/runlevels/${SOFTLEVEL}" ]
then
	eerror "ERROR:  runlevel ${SOFTLEVEL} does not exist; exiting ..."
	exit 1
else
	myscripts=
	if [ "${SOFTLEVEL}" != "${BOOTLEVEL}" ]
	then
		# Normal runlevels *include* boot scripts
		mylevels="$(dolisting "/etc/runlevels/${SOFTLEVEL}/")"
		mylevels="${mylevels} $(dolisting /etc/runlevels/${BOOTLEVEL}/)"
	else
		# Non-normal runlevels don't include boot scripts as default
		mylevels="$(dolisting "/etc/runlevels/${SOFTLEVEL}/")"
	fi
	
	for x in ${mylevels}
	do
		[ -L "${x}" ] && myscripts="${myscripts} ${x##*/}"
	done
fi

# The softscripts dir contains all scripts that belong to the
# runlevel specified in ${svcdir}/softlevel
# It needs to be a new directory, else when stopping the services
# and the old directory is not intact, things get broken

mkdir -p -m 0755 "${svcdir}/softscripts.new"

for x in ${myscripts} ; do
	if [[ ! -e /etc/init.d/${x} ]] ; then
		ewarn "WARNING:  /etc/init.d/${x} missing; skipping ..."
		continue
	fi
	# The -f eliminates a warning if the symlink already exists,
	# which can happen if a service is in both the boot level and
	# the current "normal" runlevel
	ln -snf "/etc/init.d/${x}" "${svcdir}/softscripts.new/${x}"
done

dep_stop() {
	local x=
	local dep=
	local needsme=
	local myservice="${1##*/}"
	local depservice=

	if ! service_started "${myservice}"
	then
		return 0
	fi
	
	# Candidate for zapping
	if [ ! -L "${svcdir}/softscripts.new/${myservice}" ]
	then
		# If this is a 'net' service, we do not want to stop it if it was
		# not in the previous runlevel, and we are not shutting down,
		# rebooting or going to single runlevel.  This is because the user
		# might have started it (net.ppp?), or possibly hotplug ...
		if net_service "${myservice}" && \
		   [ "${SOFTLEVEL}" != "reboot" -a \
		     "${SOFTLEVEL}" != "shutdown" ]
		then
			if [ -n "${OLDSOFTLEVEL}" ] && \
			   ! in_runlevel "${myservice}" "${OLDSOFTLEVEL}"
			then
				# This service is not in the previous runlevel, so
				# do not stop it ...
				return 0
			fi
		fi

		# Should not work for 'use'
		if [ -z "$(needsme "${myservice}")" ]
		then
			# Nothing depends on me
			stop_service "${myservice}"
		else
			# Something may depend on me
			needsme=0
			
			for dep in $(needsme "${myservice}")
			do
				if service_started "${dep}" && \
				   [ -L "${svcdir}/softscripts.new/${dep}" ]
				then
					# This dep is valid
					needsme=1
					
					break
				fi
			done
			
			if [ "${needsme}" -eq 0 ]
			then
				stop_service "${myservice}"
			fi
		fi
	fi
}

# Stop services
if [[ ${SOFTLEVEL} != "reboot" && \
      ${SOFTLEVEL} != "shutdown" ]]
then
	for i in $(dolisting "${svcdir}/started/") ; do
		dep_stop "${i}"
	done
else
	get_critical_services

	is_critical_service() {
		local x
		local myservice=${1##*/}

		for x in ${CRITICAL_SERVICES} ; do
			[[ ${myservice} == "${x}" ]] && return 0
		done

		return 1
	}
	
	# First stop non critical services
	for i in $(dolisting "${svcdir}/started/")
	do
		if [ -n "${LOGGER_SERVICE}" ]
		then
			# Only stop it if the logger do not depends on it
			if ! query_before "${i##*/}" "${LOGGER_SERVICE}"
			then
				continue
			fi
		fi

		# Do not stop critical services just yet
		is_critical_service "${i}" || dep_stop "${i}"
	done

	# Now stop the logger if running
	if [ -n "${LOGGER_SERVICE}" ]
	then
		dep_stop "${LOGGER_SERVICE}"
	fi

	# Now stop the rest
	for i in $(dolisting "${svcdir}/started/")
	do
		dep_stop "${i}"
	done
fi

# Only change softlevel AFTER all the services have been stopped,
# else they will not get the depend's right (wrong SOFTLEVEL)

echo "${SOFTLEVEL}" > "${svcdir}/softlevel"

if [[ ${SOFTLEVEL} == "reboot" || ${SOFTLEVEL} == "shutdown" ]] ; then
	source /sbin/functions.sh
	
	# Clear $svcdir from stale entries, but leave the caches around, as it
	# should help speed things up a bit
	rm -rf $(ls -d1 "${svcdir}/"* 2>/dev/null | \
	         grep -ve '\(depcache\|deptree\|envcache\)')
	
	source /etc/init.d/halt.sh
	
	if [[ ${SOFTLEVEL} == "reboot" ]] ; then
		source /etc/init.d/reboot.sh
	else
		source /etc/init.d/shutdown.sh
	fi
	
	# Should never get here
	exit 0
fi

# Move the old softscritps directory to a different one
# and make the new softscripts directory the current

mv -f "${svcdir}/softscripts" "${svcdir}/softscripts.old"
mv -f "${svcdir}/softscripts.new" "${svcdir}/softscripts"

dep_start() {
	local myservice="${1##*/}"

	[ ! -L "${svcdir}/softscripts/${myservice}" ] && continue

	# Only start a script if it isn't already running
	service_started "${myservice}" || schedule_service_startup "${myservice}"
}

get_critical_services

EXTRA_SOFTSCRIPTS="${CRITICAL_SERVICES}"

if [ -n "${LOGGER_SERVICE}" -a -L "${svcdir}/softscripts/${LOGGER_SERVICE}" ]
then
	service_started "${LOGGER_SERVICE}" || \
		EXTRA_SOFTSCRIPTS="${EXTRA_SOFTSCRIPTS} ${LOGGER_SERVICE}"
fi

if [ "${SOFTLEVEL}" != "${BOOTLEVEL}" ]
then
	for i in $(dolisting "/etc/runlevels/${BOOTLEVEL}/")
	do
		[ -L "${svcdir}/softscripts/${i##*/}" ] && \
			EXTRA_SOFTSCRIPTS="${EXTRA_SOFTSCRIPTS} ${i##*/}"
	done
fi

# Start scripts
for i in ${EXTRA_SOFTSCRIPTS} $(dolisting "${svcdir}/softscripts/")
do
	dep_start "${i##*/}"
done

# Wait for any services that may still be running ...
[ "${RC_PARALLEL_STARTUP}" = "yes" ] && wait

# Clean the old runlevel
rm -rf "${svcdir}/softscripts.old" &>/dev/null

# Runlevel end, so clear stale fail list
rm -rf "${svcdir}/failed" &>/dev/null

# If we were in the boot runlevel, it is done now ...
[ -n "${BOOT}" ] && unset BOOT

# vim:ts=4
