#!/bin/bash
# Copyright 1999-2003 Gentoo Technologies, Inc.
# Distributed under the terms of the GNU General Public License v2
# $Header: /home/cvsroot/gentoo-src/rc-scripts/sbin/rc,v 1.69 2003/12/26 00:25:24 azarah Exp $


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

try() {
	local errstr=
	local retval=0
	# This works fine in test, but real life booting, fails for mounting /proc
	# if we only check $? for instance ...  We thus need to [ -n "${errstr}" ]
	# as well.
	
	errstr="$((eval $*) 2>&1 >/dev/null)"
	retval=$?
	if [ "${retval}" -ne 0 ] || \
	   ([ "${*/mount/}" != "$*" -a -n "${errstr}" ])
	then
		# Progressbar begin
		rc_splash "stop" &> /dev/null &
		# Progressbar end

		echo -e "${ENDCOL}${NORMAL}[${BAD} oops ${NORMAL}]"
		echo
		eerror "The \"${1}\" command failed with error:"
		echo
		echo "${errstr#*: }"
		echo
		eerror "Since this is a critical task, startup cannot continue."
		echo
		/sbin/sulogin ${CONSOLE}
		einfo "Unmounting filesystems"
		/bin/mount -a -o remount,ro & >/dev/null
		einfo "Rebooting"
		/sbin/reboot -f
	fi
	
	return ${retval}
}

# 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 Linux to function properly, \"$1\" need to exist."
			eerror "Please mount your root partition read/write, and execute:"
			echo
			eerror "  # mkdir -p $1"
			echo; echo
			/sbin/sulogin ${CONSOLE}
			einfo "Unmounting filesystems"
			/bin/mount -a -o remount,ro & >/dev/null
			einfo "Rebooting"
			/sbin/reboot -f
		fi
	fi

	return 0
}

# From initscript by Robert Love
populate_udev() {
	local x=
	local y=
	local CLASS=
	
	# Propogate /dev from /sys - we only need this while we do not
	# have initramfs and an early user-space with which to do early
	# device bring up
	export ACTION=add
	# Hack to get udev not to sleep - its not needed, as here all
	# /sys entries are already present.
	export UDEV_NO_SLEEP=1
	
	# Add block devices and their partitions
	for x in /sys/block/*
	do
		# Add each drive
		export DEVPATH="${x#/sys}"
		/sbin/udev block
		
		# Add each partition, on each device
		for y in ${x}/*
		do
			if [ -f "${y}/dev" ]
			then
				export DEVPATH="${y#/sys}"
				/sbin/udev block
			fi
		done
	done
	
	# All other device classes
	for x in /sys/class/*
	do
		for y in ${x}/*
		do
			if [ -f "${y}/dev" ]
			then
				CLASS="$(echo "${x#/sys}" | cut --delimiter='/' --fields=3-)"
				export DEVPATH="${y#/sys}"
				/sbin/udev ${CLASS}
			fi
		done
	done

	unset ACTION DEVPATH UDEV_NO_SLEEP

	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="checkroot hostname modules checkfs localmount"
	fi

	export CRITICAL_SERVICES

	return 0
}

# Save $1
argv1="$1"

# Progressbar begin
rc_splash_init
# Progressbar end

# 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 [ "${RUNLEVEL}" = "S" -a "${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 2001-2003 Gentoo Technologies, Inc.; Distributed under the GPL"
	echo

	check_statedir /proc

	ebegin "Mounting proc at /proc"
	try try mount -n -t proc none /proc
	eend $?

	if [ -d /sys -a "$(get_KV)" -ge "$(KV_to_int '2.6.0')" ]
	then
		ebegin "Mounting sysfs at /sys"
		mount -t sysfs none /sys
		eend $?

	elif [ "$(get_KV)" -ge "$(KV_to_int '2.6.0')" ]
	then
		ewarn "No /sys to mount sysfs needed in 2.6 and later kernels!"
	fi

	check_statedir /dev

	devfs="yes"
	udev="yes"

	# Check if we can use udev, and that the user did not disable
	# it explicitly ...
	if get_bootparam "noudev" || \
	   [ ! -x /sbin/udev -o -e "/dev/.devfsd" ] || \
	   [ "$(get_KV)" -lt "$(KV_to_int '2.6.0')" ]
	then
		udev="no"
	fi

	# Check if the user disabled devfs explicitly
	if get_bootparam "nodevfs" || [ "${udev}" = "yes" ]
	then
		devfs="no"
	fi
	
	# Fix weird bug where there is a /dev/.devfsd in a unmounted /dev
	mymounts="$(awk '($3 == "devfs") { print "yes"; exit 0 }' /proc/mounts)"
	if [ -e "/dev/.devfsd" -a "${mymounts}" != "yes" ]
	then
		rm -f /dev/.devfsd
	fi

	if [ "${udev}" = "yes" ]
	then
		ebegin "Mounting ramfs at /dev"
		try mount -n -t ramfs none /dev
		eend $?
		ebegin "Configuring system to use udev"
		einfo "  Populating /dev with device nodes..."
		try tar -jxpf /lib/udev-state/devices.tar.bz2 -C /dev
		populate_udev
		if [ -e /proc/sys/kernel/hotplug -a -x /sbin/hotplug ]
		then
			einfo "  Using /sbin/hotplug for udev management..."
			
		elif [ -e /proc/sys/kernel/hotplug ]
		then
			einfo "  Setting /sbin/udev as hotplug agent..."
			echo "/sbin/udev" > /proc/sys/kernel/hotplug
		else
			ewarn "  Kernel was not compiled with hotplug support!"
		fi
		eend 0
		# Create problematic directories
		mkdir -p /dev/{pts,shm}
		# Same thing as /dev/.devfsd
		touch /dev/.udev

	# With the new way, /dev can be mounted by the kernel ...
	elif [ "${devfs}" = "yes" -a ! -e "/dev/.devfsd" ]
	then
		mymounts="$(awk '($2 == "devfs") { print "yes"; exit 0 }' /proc/filesystems)"
		# Is devfs support compiled in?
		if [ "${mymounts}" = "yes" ]
		then
			ebegin "Mounting devfs at /dev"
			try mount -n -t devfs none /dev
			eend $?
		else
			clear
			echo
			einfo "The Gentoo Linux system initialization scripts have detected that your"
			einfo "kernel has been compiled without DEVFS support.  Because Gentoo Linux"
			einfo "has been designed to work with DEVFS, it is required that you compile"
			einfo "support for it into your kernel.  Please read the 'Gentoo Linux from"
			einfo "source (build) CD Installation Guide' at:"
			echo
			einfo "    http://www.gentoo.org/doc/build.html"
			echo
			einfo "for more information on how to do this."
			echo
			read -t 15 -p "(hit Enter to continue or wait 15 seconds...)"
		fi
	fi

	# From linux-2.5.68 we need to mount /dev/pts again ...
	if [ "$(get_KV)" -ge "$(KV_to_int '2.5.68')" ]
	then
		have_devpts="$(awk '($2 == "devpts") { print "yes"; exit 0 }' /proc/filesystems)"
		mymounts="$(awk '($3 == "devfs") { print "yes"; exit 0 }' /proc/mounts)"

		if [ "${have_devpts}" = "yes" ]
		then
			# Only try to create /dev/pts if we have /dev mounted as devfs,
			# else it might fail as / might be still mounted readonly.
			if [ ! -d /dev/pts -a -e "/dev/.devfsd" -a "${mymounts}" = "yes" ]
			then
				# Make sure we have /dev/pts
				mkdir -p /dev/pts &>/dev/null || \
					ewarn "Could not create /dev/pts!"
				
			fi

			if [ -d /dev/pts ]
			then
				ebegin "Mounting devpts at /dev/pts"
				try mount -n -t devpts none /dev/pts
				eend $?
			fi
		fi
	fi

	# Need devfsd running now so that /dev/ram0 exists if we need it
	mymounts="$(awk '($3 == "devfs") { print "yes"; exit 0 }' /proc/mounts)"
	if [ "${devfs}" = "yes" -a -e "/dev/.devfsd" -a \
	     "${mymounts}" = "yes" ]
	then
		if [ "${RC_DEVFSD_STARTUP}" != "no" ]
		then
			ebegin "Starting devfsd"
			/sbin/devfsd /dev > /dev/null
			eend $?
		fi
	fi

	# Update splash wrappers.
	update_splash_wrappers
	# Progressbar begin
	rc_splash "text"
	# Progressbar end

	if [ -x /sbin/irqbalance -a "$(get_KV)" -ge "$(KV_to_int '2.5.0')" ]
	then
		ebegin "Starting irqbalance"
		/sbin/irqbalance
		eend $?
	fi

	# Mount either a ramdisk or tmpfs if requested.
	# we do this to temp mount, until we can get /var mounted ...
	tsvcdir="/mnt/.init.d"

	# Should we use tmpfs or a ramdisk for caching dependency and
	# general initscript data?  Note that the 'gentoo=tmpfs' kernel
	# option should override most settings ...
	if get_bootparam "tmpfs" || \
	   [ "${svcmount}" = "yes" -a "${svcfstype}" = "tmpfs" ]
	then
		check_statedir "${tsvcdir}"
		
		ebegin "Mounting tmpfs at ${tsvcdir}"
		try mount -n -t tmpfs tmpfs "${tsvcdir}" \
			-o rw,mode=0644,size="${svcsize}"k
		eend 0
		
	elif get_bootparam "ramfs" || \
	     [ "${svcmount}" = "yes" -a "${svcfstype}" = "ramfs" ]
	then
		check_statedir "${tsvcdir}"

		ebegin "Mounting ramdisk 0 at ${tsvcdir}"
		try dd if=/dev/zero of=/dev/ram0 bs=1k count="${svcsize}"
		try /sbin/mke2fs -i 1024 -vm0 /dev/ram0 "${svcsize}"
		try mount -n -t ext2 /dev/ram0 "${tsvcdir}" -o rw
		eend 0
	fi

	# Swap needs to be activated *after* devfs has been mounted and *after*
	# devfsd has been started, so that the fstab can be properly parsed
	# and only if the server/Gentoo box is initialized ...
	ebegin "Activating (possible) swap"
	/sbin/swapon -a &> /dev/null
	eend 0

	# Set the console loglevel to 1 for a cleaner boot
	# the logger should anyhow dump the ring-0 buffer at start to the
	# logs, and that with dmesg can be used to check for problems
	/bin/dmesg -n 1

	# 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=
			
		source "/etc/init.d/${x}" || eerror "Failed to source /etc/init.d/${x}"
		retval=$?
		[ "${retval}" -ne 0 ] && return "${retval}"
			
		start || eerror "Failed to start /etc/init.d/${x}"
		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
		# Progressbar begin
		rc_splash_update "inline" "${x}" "start"
		# Progressbar end

		if ! start_critical_service "${x}"
		then
			# Progressbar begin
			rc_splash "stop" &> /dev/null &
			# Progressbar end
			
			echo
			eerror "One of more critical startup scripts failed to start!"
			eerror "Please correct this, and reboot ..."
			echo; echo
			/sbin/sulogin ${CONSOLE}
			einfo "Unmounting filesystems"
			/bin/mount -a -o remount,ro & >/dev/null
			einfo "Rebooting"
			/sbin/reboot -f
		fi
	done
	# Check that $svcdir exists ...
	check_statedir "${svcdir}"

	# Since we are using tmpfs or ramfs, just bin to $tsvcdir to $svcdir
	if get_bootparam "tmpfs" || get_bootparam "ramfs" || [ "${svcmount}" = "yes" ]
	then
		mount --bind "${tsvcdir}" "${svcdir}"
	fi

	# Clear $svcdir from stale entries
	rm -rf "${svcdir}"/*

	# Update the dependency cache
	/sbin/depscan.sh

	# 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
	)
fi # Sysinit ends here

# Update splash wrappers.
update_splash_wrappers

if [ "${RUNLEVEL}" = "S" -a "${argv1}" = "sysinit" ]
then
	# Progressbar begin
	rc_splash_debug "${argv1}"
	# Progressbar end
	exit 0

elif [ "${RUNLEVEL}" = "S" -a "${argv1}" = "boot" ]
then
	setup_defaultlevels

	if [ -n "${DEFAULTLEVEL}" -a "${DEFAULTLEVEL}" != "default" ]
	then
		# Setup our default runlevel runlevel that will be run
		# the first time /sbin/rc is called with argv1 != sysinit|boot
		echo "${DEFAULTLEVEL}" > "${svcdir}/ksoftlevel"
	fi
	
	# $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}"
	
elif [ "${RUNLEVEL}" != "S" -a -e "${svcdir}/ksoftlevel" ]
then
	argv1="$(< ${svcdir}/ksoftlevel)"
	rm -f "${svcdir}/ksoftlevel"
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

# Progressbar begin
rc_splash_calc
# Progressbar end

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

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

elif [ "${SOFTLEVEL}" = "single" ]
then
	get_critical_services

	myscripts="${CRITICAL_SERVICES}"
	
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
	
	[ "${OLDSOFTLEVEL}" = "${BOOTLEVEL}" -o "${OLDSOFTLEVEL}" = "single" ] \
		&& /bin/dmesg -n 1
	
	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

install -d -m0755 "${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 [ "${myservice%%.*}" = "net" -a \
		     "${myservice##*.}" != "${myservice}" ] && \
		   [ "${SOFTLEVEL}" != "reboot" -a \
		     "${SOFTLEVEL}" != "shutdown" -a \
		     "${SOFTLEVEL}" != "single" ]
		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}" != "single" -a \
     "${SOFTLEVEL}" != "reboot" -a \
     "${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
			if [ "${myservice}" = "${x}" ]
			then
				return 0
			fi
		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" -o "${SOFTLEVEL}" = "shutdown" ]
then
	source /sbin/functions.sh
	
	# Progressbar begin
	rc_splash_debug "${SOFTLEVEL}"
	# Progressbar end

	# Make sure that our $svcdir are clean for next reboot ...
	rm -rf "${svcdir}"/*
	
	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

# Depends gets nuked, so update them
# (this problem should be solved now, but i think it will be a good idea
#  to recreate the deps after a change in runlevel)

#/sbin/depscan.sh &>/dev/null

# We want devfsd running after a change of runlevel (this is mostly if we return
# from runlevel 'single')
if [ -z "`ps --no-heading -C 'devfsd'`" -a \
     -n "`gawk '/\/dev devfs/ { print }' /proc/mounts 2> /dev/null`" ]
then
	if [ "${RC_DEVFSD_STARTUP}" != "no" ]
	then
		/sbin/devfsd /dev &> /dev/null
	fi
fi

# 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

# Progressbar begin
rc_splash_debug "${SOFTLEVEL}"
# Progressbar end


# vim:ts=4
