#!/bin/bash
################################################################################
#                                                                              #
#  Author: Sean E. Russell <ser@germane-software.com>                          #
#  Version: 1.0                                                                #
#  Date: Jun 26, 2002                                                          #
#  Adaptation: Mike Frysinger [SpanKY] <vapier@gentoo.org>                     #
#      Original code was in Ruby ... recoded into bash (at syntax level)       #
#                                                                              #
#  This application displays information about the RC system used by Gentoo.   #
#  In particular, it displays a tree-like format of a run level, showing       #
#  all of the services that are installed at that level, and what each         #
#  service's status is (running, stopped, etc.)                                #
#                                                                              #
#  -a can be used to display all runlevels                                     #
#  -d can be used to display service dependancies                              #
#  -u will display all unassigned services                                     #
#  -s will display all services                                                #
#  -h will display help                                                        #
#  <runlevel> is used to choose the run level for which information is         #
#             displayed                                                        #
#                                                                              #
#  By default, rc-status only displays information about the current           #
#  runlevel; services installed and services running.                          #
#                                                                              #
################################################################################

# grab code from functions.sh so we don't have to reproduce it
source /sbin/functions.sh
runleveldir=/etc/runlevels

# grab settings from conf.d/rc
source /etc/conf.d/rc

################################################################################
#  Parse command line options                                                  #
################################################################################
do_opt() {
	case $1 in
		--all|-a)
			ALL=true
			;;
		--depend)
			DEPEND=true
			;;
		--unused|-u)
			ALL=true
			UNUSED=true
			;;
		--list|-l)
			ls -1 ${runleveldir}
			exit 0
			;;
		--servicelist|-s)
			ALL=true
			SERVICELIST=true
			;;
		--nocolor|-nc)
			;;
		--help|-h|-*)
			echo "USAGE: $0 [command | <runlevel>]"
			echo
			echo "Commands:"
			echo "  -a, --all          Show services at all run levels"
			echo "  -l, --list         Show list of run levels"
			echo "  -u, --unused       Show services not assigned to any run level"
			echo "  -s, --servicelist  Show service list"
			echo "  -nc,--nocolor      Monochrome output only"
			echo "  <runlevel>         Show services assigned to <runlevel>"
			echo
			echo "If no arguments are supplied, shows services for current run level."
			exit 0
			;;
		*)
			runlevel=$1
			;;
	esac
}
for opt in "$@" ; do
	do_opt ${opt}
	[[ -n $2 ]] && shift
done

################################################################################
#  Find the current runlevel being queried.  This is either something supplied #
#  on the command line, or pulled from softlevel                               #
################################################################################
if [[ -z "${runlevel}" ]] ; then
	if [[ -e "${svcdir}/softlevel" ]] ; then
		runlevel="$( <${svcdir}/softlevel )"
	else
		ewarn "Could not local current runlevel in ${svcdir}/softlevel"
		if [[ -d "${runleveldir}/single" ]] ; then
			runlevel=single
		elif [[ -d "${runleveldir}/default" ]] ; then
			runlevel=default
		else
			eerror "Your installation is probably broken ... please \`emerge baselayout\`"
			exit 1
		fi
		ewarn "Assuming current runlevel is '${runlevel}'"
	fi
fi
if [[ ! -d "${runleveldir}/${runlevel}" ]] ; then
	eerror "${runlevel} is not a valid run level !"
	eerror "Valid runlevels (obtained from \`rc-status --list\`):"
	rc-status --list
	exit 1
fi

################################################################################
# Build up a hash of the services associated with each run level.  In the most #
# trivial case, this is simply the current runlevel.  If --all was specified,  #
# we gather information about all of the runlevels.  If --unused was           #
# specified, we pull info about all of the services and filter for the ones    #
# that don't appear in any runlevel.                                           #
################################################################################
runlevelidxs=$( ls ${runleveldir} )
declare -a runlevels
# For each directory in /etc/runlevels, do ...
arridx=0
for level in ${runlevelidxs} ; do
	if [[ ${level} == ${runlevel} || -n ${ALL} ]] ; then
		runlevels[${arridx}]=$( find ${runleveldir}/${level} -maxdepth 1 -type l -printf '%f ' )
		let "arridx += 1"
	fi
done

# In case --all was specified, get a list of all the services set up in
# /etc/init.d; services can be added, but not enabled, and we need to
# identify these 'orphan' services.
in_list() { #$1=list  $2=find
	for ele in $1 ; do
		if [[ ${ele} == $2 ]] ; then
			echo 1
			return 0
		fi
	done
	echo 0
	return 0
}
if [[ -n ${ALL} ]] ; then
	unassigned=
	allservices=
	for service in $( ls -1 /etc/init.d | grep -v '\.sh$' ) ; do
		if [[ $( in_list "${runlevels[*]}" "${service}" ) -eq 0 ]] ; then
			unassigned="${unassigned} ${service}"
		fi
		allservices="${allservices} ${service}"
	done
	runlevelidxs="${runlevelidxs} UNASSIGNED"
	runlevels[${arridx}]="${unassigned}"
	runlevels[${arridx}+1]="${allservices}"
fi

################################################################################
#  Now collect information about the status of the various services; whether   #
#  they're started, broken, or failed.  Put all of this into arrays.           #
################################################################################
# Read services from ${svcdir}/{started,failed,broken}
[[ -x "${svcdir}/starting" ]] && starting=$( ls ${svcdir}/starting )
[[ -x "${svcdir}/inactive" ]] && inactive=$( ls ${svcdir}/inactive )
[[ -x "${svcdir}/started" ]] && started=$( ls ${svcdir}/started )
[[ -x "${svcdir}/stopping" ]] && stopping=$( ls ${svcdir}/stopping )
[[ -x "${svcdir}/failed"  ]] && failed=$( ls ${svcdir}/failed )
[[ -x "${svcdir}/broken"  ]] && broken=$( ls ${svcdir}/broken )

################################################################################
#  Now print out the information we've gathered.  We do this by going through  #
#  the hash of 'runlevels' information, and for each String key/Array value    #
#  pair, print the runlevel; then for each service in that runlevel, print the #
#  service name and its status.                                                #
################################################################################
# Define a helper method for printing the status of a service; '[ xxx ]'
print_msg() {
	printf " %-$((COLS - 5 - ${#3}))s%s\n" "$1" "${BRACKET}[ $2$3 ${BRACKET}]${NORMAL}"
}

# if --all wasnt specified, dont print everything
[[ -z ${ALL} ]] && runlevelidxs=${runlevel}
if [[ -z ${UNUSED} ]] ; then
	if [[ -z ${SERVICELIST} ]] ; then
		arridx=0
	else
		runlevelidxs="all"
		let "arridx += 1"
	fi
else
	runlevelidxs="unused"
fi

for level in ${runlevelidxs} ; do
	echo "Runlevel: ${HILITE}${level}${NORMAL}"
	for service in ${runlevels[${arridx}]} ; do
		if [[ -n ${inactive} && $( in_list "${inactive}" "${service}" ) -eq 1 ]] ; then
			print_msg "${service}" "${WARN}" 'inactive'
		elif [[ $(in_list "${started}" "${service}") -eq 1 ]] ; then
			print_msg "${service}" "${GOOD}" 'started'
		elif [[ -n ${starting} && $( in_list "${starting}" "${service}" ) -eq 1 ]] ; then
			print_msg "${service}" "${GOOD}" 'starting'
		elif [[ -n ${stopping} && $( in_list "${stopping}" "${service}" ) -eq 1 ]] ; then
			print_msg "${service}" "${BAD}" 'stopping'
		elif [[ -n ${failed} && $( in_list "${failed}" "${service}" ) -eq 1 ]] ; then
			print_msg "${service}" "${BAD}" 'failed'
		elif [[ -n ${broken} && $( in_list "${broken}" "${service}" ) -eq 1 ]] ; then
			print_msg "${service}" "${BAD}" 'broken'
		else
			print_msg "${service}" "${WARN}" '  off'
		fi
	done
	let "arridx += 1"
	[ -n "${UNUSED}" ] && exit 0
done
