#!/bin/bash
# Copyright 1999-2004 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.80 2004/04/25 22:31:48 agriffis Exp $


trap ":" INT QUIT TSTP
source /sbin/functions.sh
# Only source this when this is a livecd booting ... 
[ -f /sbin/livecd-functions.sh ] && source /sbin/livecd-functions.sh
umask 022

try() {
	local errstr
	local retval=0
	
	if [ -c /dev/null ]; then
		errstr="$((eval $*) 2>&1 >/dev/null)"
	else
		errstr="$((eval $*) 2>&1)"
	fi
	retval=$?
	if [ "${retval}" -ne 0 ]
	then
		# Progressbar begin
		if [ -c /dev/null ]; then
			rc_splash "stop" &>/dev/null &
		else
			rc_splash "stop" &
		fi
		# 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"
		if [ -c /dev/null ]; then
			/bin/mount -a -o remount,ro & >/dev/null
		else
			/bin/mount -a -o remount,ro
		fi
		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
}

populate_udev() {
	/sbin/udevstart

	# Not provided by sysfs but needed
	ln -snf /proc/self/fd /dev/fd
	ln -snf fd/0 /dev/stdin
	ln -snf fd/1 /dev/stdout
	ln -snf fd/2 /dev/stderr
	ln -snf /proc/kcore /dev/core
	ln -snf /proc/asound/oss/sndstat /dev/sndstat

	# Create problematic directories
	mkdir -p /dev/{pts,shm}

	# Same thing as /dev/.devfsd
	touch /dev/.udev

	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}"

	if [ ! -c /dev/null ] ; then
		echo -e "${BAD}Your root filesystem is missing /dev/null${NORMAL}"
		echo -e "${BAD}You should really get this sorted out.${NORMAL}"
	fi

	echo
	echo -e "${GOOD}Gentoo Linux${GENTOO_VERS}; ${BRACKET}http://www.gentoo.org/${NORMAL}"
	echo -e " Copyright 2001-2004 Gentoo Technologies, Inc.; Distributed under the GPL"
	echo

	check_statedir /proc

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

	# Read off the kernel commandline to see if there's any special settings
	# especially check to see if we need to set the  CDBOOT environment variable
	# Note: /proc MUST be mounted
	[ -f /sbin/livecd-functions.sh ] && livecd_read_commandline

	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"
		if [ "${RC_DEVICE_TARBALL}" = "yes" ]
		then
			einfo "  Populating /dev with device nodes..."
			try tar -jxpf /lib/udev-state/devices.tar.bz2 -C /dev
		fi
		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

	# 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 -o gid=5,mode=0620 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

	# If booting off CD, we want to update inittab before setting the runlevel
	if [ -f "/sbin/livecd-functions.sh" -a -n "${CDBOOT}" ]
	then
		ebegin "Updating inittab"
		livecd_fix_inittab
		eend $?
		/sbin/telinit q &>/dev/null
	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
	mkdir -p -m 0755 "${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

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 [ "${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
