#!/bin/bash
# initscripts functions
#

# width:

STAT_COL=80
if [[ ! -t 1 ]]; then
    USECOLOR=""
elif [[ -t 0 ]]; then
    # stty will fail when stdin isn't a terminal
    STAT_COL="$(/bin/stty size)"
    # stty gives "rows cols"; strip the rows number, we just want columns
    STAT_COL="${STAT_COL##* }"
elif /bin/tput cols &>/dev/null; then
    # is /usr/share/terminfo already mounted, and TERM recognized?
    STAT_COL=$(/bin/tput cols)
fi
if ((STAT_COL==0)); then
    # if output was 0 (serial console), set default width to 80
    STAT_COL=80
    USECOLOR=""
fi

# we use 13 characters for our own stuff
STAT_COL=$(($STAT_COL - 13))

# disable colors on broken terminals
TERM_COLORS="$(/bin/tput colors 2>/dev/null)"
if (($? != 3)); then
    case $TERM_COLORS in
	*[!0-9]*) USECOLOR="";;
	[0-7])    USECOLOR="";;
	'')       USECOLOR="";;
    esac
fi
unset TERM_COLORS

# clear the TZ envvar, so daemons always respect /etc/localtime
unset TZ

# sanitize the locale settins
unset LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY \
      LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE \
      LC_MEASUREMENT LC_IDENTIFICATION LC_ALL
if [[ $DAEMON_LOCALE =~ yes|YES && -n $LOCALE ]]; then
	export LANG="${LOCALE}"
else
	export LANG=C
fi

# colors:
if [[ $USECOLOR = YES || $USECOLOR = yes ]]; then
	C_MAIN="\033[1;37;40m"      # main text

	C_OTHER="\033[1;34;40m"     # prefix & brackets
	C_SEPARATOR="\033[1;30;40m" # separator

	C_BUSY="\033[0;36;40m"      # busy
	C_FAIL="\033[1;31;40m"      # failed
	C_DONE="\033[1;37;40m"      # completed
	C_BKGD="\033[1;35;40m"      # backgrounded

	C_H1="\033[1;37;40m"        # highlight text 1
	C_H2="\033[1;36;40m"        # highlight text 2

	C_CLEAR="\033[1;0m"
fi

if [[ -t 1 ]]; then
	SAVE_POSITION="\033[s"
	RESTORE_POSITION="\033[u"
	DEL_TEXT="\033[$(($STAT_COL+4))G"
else
	SAVE_POSITION=""
	RESTORE_POSITION=""
	DEL_TEXT=""
fi

# prefixes:

PREFIX_REG="::"
PREFIX_HL=" >"

# functions:

deltext() {
	printf "${DEL_TEXT}"
}

printhl() {
	printf "${C_OTHER}${PREFIX_HL} ${C_H1}${1}${C_CLEAR} \n"
}

printsep() {
	printf "\n${C_SEPARATOR}   ------------------------------\n"
}

stat_bkgd() {
	printf "${C_OTHER}${PREFIX_REG} ${C_MAIN}${1}${C_CLEAR} "
	deltext
	printf "   ${C_OTHER}[${C_BKGD}BKGD${C_OTHER}]${C_CLEAR} "
}

stat_busy() {
	printf "${C_OTHER}${PREFIX_REG} ${C_MAIN}${1}${C_CLEAR} "
	printf "${SAVE_POSITION}"
	deltext
	printf "   ${C_OTHER}[${C_BUSY}BUSY${C_OTHER}]${C_CLEAR} "
}

stat_append() {
	printf "${RESTORE_POSITION}"
	printf -- "${C_MAIN}${1}${C_CLEAR}"
	printf "${SAVE_POSITION}"
}

stat_done() {
	deltext
	printf "   ${C_OTHER}[${C_DONE}DONE${C_OTHER}]${C_CLEAR} \n"
}

stat_fail() {
	deltext
	printf "   ${C_OTHER}[${C_FAIL}FAIL${C_OTHER}]${C_CLEAR} \n"
}

stat_die() {
	stat_fail
	exit ${1:-1}
}

status() {
	stat_busy "$1"
	shift
	if "$@" >/dev/null 2>&1; then
		stat_done
		return 0
	fi
	stat_fail
	return 1
}

#  usage : in_array( $needle, $haystack )
# return : 0 - found
#          1 - not found
# Copied from makepkg
in_array() {
    [[ $2 ]] || return 1
    local needle=$1; shift
    local item
    for item in "$@"; do
	[[ ${item#@} = $needle ]] && return 0
    done
    return 1 # Not Found
}

# daemons:

add_daemon() {
	[[ -d /var/run/daemons ]] || /bin/mkdir -p /var/run/daemons
	> /var/run/daemons/"$1"
}

rm_daemon() {
	/bin/rm -f /var/run/daemons/"$1"
}

ck_daemon() {
	[[ ! -f /var/run/daemons/$1 ]]
}

have_daemon() {
    [[ -x /etc/rc.d/$1 ]]
}

start_daemon() {
    have_daemon "$1" && /etc/rc.d/"$1" start
}

ck_depends() {
    for daemon in "$@"; do
	ck_daemon "$daemon" && start_daemon "$daemon"
    done
}

start_daemon_bkgd() {
	stat_bkgd "Starting $1"
	have_daemon "$1" && (start_daemon "$1") &>/dev/null &
}

stop_daemon() {
	have_daemon "$1" && /etc/rc.d/"$1" stop
}

# Status functions
status_started() {
  deltext
  echo -ne "$C_OTHER[${C_STRT}STARTED$C_OTHER]$C_CLEAR "
}

status_stopped() {
  deltext
  echo -ne "$C_OTHER[${C_STRT}STOPPED$C_OTHER]$C_CLEAR "
}

ck_status() {
  if ! ck_daemon "$1"; then
    status_started
  else
    status_stopped
  fi
}

kill_everything() {
    # $1 = where we are being called from.
    # This is used to determine which hooks to run.
    # Find daemons NOT in the DAEMONS array. Shut these down first
    for daemon in /var/run/daemons/*; do
        [[ -f $daemon ]] || continue
        daemon=${daemon##*/}
	in_array "$daemon" "${DAEMONS[@]}" || stop_daemon "$daemon"
    done

    # Shutdown daemons in reverse order
    for ((i=${#DAEMONS[@]}-1; i>=0; i--)); do
	[[ ${DAEMONS[$i]:0:1} = '!' ]] && continue
	ck_daemon ${DAEMONS[$i]#@} || stop_daemon ${DAEMONS[$i]#@}
    done

	# Terminate all processes
    stat_busy "Sending SIGTERM To Processes"
    run_hook "$1_prekillall"
    /sbin/killall5 -15 &> /dev/null
    /bin/sleep 5
    stat_done

    stat_busy "Sending SIGKILL To Processes"
    /sbin/killall5 -9 &> /dev/null
    /bin/sleep 1
    stat_done

    run_hook "$1_postkillall"
}

activate_vgs() {
    [[ $USELVM =~ yes|YES && -x /sbin/lvm && -d /sys/block ]] || return
    # Kernel 2.6.x, LVM2 groups
    /sbin/modprobe -q dm-mod 2>/dev/null
    stat_busy "Activating LVM2 groups"
    if /sbin/vgchange --sysinit -a y >/dev/null; then
	stat_done
    else
	stat_fail
     fi
}

# Arch cryptsetup packages traditionally contained the binaries
#  /usr/sbin/cryptsetup
#  /sbin/cryptsetup.static
# By default, initscripts used the /sbin/cryptsetup.static.
# Newer packages will only have /sbin/cryptsetup and no static binary
# This ensures maximal compatibility with the old and new layout
for CS in /sbin/cryptsetup /usr/sbin/cryptsetup \
    /sbin/cryptsetup.static ''; do
    [[ -x $CS ]] && break
done

read_crypttab() {
    # $1 = function to call with the split out line from the crypttab
    local line nspo failed=0
    while read line; do
        [[ $line && ${line:0:1} != '#' ]] || continue
        eval nspo=("${line%#*}")
        if $1 "${nspo[0]}" "${nspo[1]}" "${nspo[2]}" "${nspo[*]:3}"; then
          crypto_unlocked=1
        else
          failed=1
        fi
    done < /etc/crypttab
    return $failed
}

###############################
# Custom hooks in initscripts #
###############################
# Hooks can be used to include custom code in various places in the rc.* scripts
#
# Define a hook function in a functions.d file using:
#  function_name() {
#    ...
#  }
#  add_hook hook_name function_name
# It is allowed to register several hook functions for the same hook
# Is is also allowed to register the same hook function for several hooks
#
# Currently, the following hooks exist:
# sysinit_start: at the beginning of rc.sysinit
# multi_start: at the beginning of rc.multi
# single_start: at the beginning of rc.single
# shutdown_start: at the beginning of rc.shutdown
# sysinit_end: at the end of rc.sysinit
# multi_end: at the end of rc.multi
# single_end: at the end of rc.single
# sysinit_udevlaunched: after udev has been launched in rc.sysinit
# single_udevlaunched: after udev has been launched in rc.single
# sysinit_udevsettled: after uevents have settled in rc.sysinit
# single_udevsettled: after uevents have settled in rc.single
# sysinit_premount: before local filesystems are mounted, but after root is mounted read-write in rc.sysinit
# shutdown_prekillall: before all processes are being killed in rc.shutdown
# single_prekillall: before all processes are being killed in rc.single
# shutdown_postkillall: after all processes have been killed in rc.shutdown
# single_postkillall: after all processes have been killed in rc.single
# shutdown_poweroff: directly before powering off in rc.shutdown
#
# Declare add_hook and run_hook as read-only to prevent overwriting them.
# Too bad we cannot do the same thing with hook_funcs

if [[ $RC_FUNCTIONS_HOOK_FUNCS_DEFINED -ne 1 ]]; then
	declare -A hook_funcs

	add_hook() {
		[[ $1 && $2 ]] || return 1
		hook_funcs["$1"]+=" $2"
	}

	run_hook() {
		[[ $1 ]] || return 1
		local func
		for func in ${hook_funcs["$1"]}; do
			"${func}"
		done
	}

	declare -fr add_hook run_hook
	declare -r RC_FUNCTIONS_HOOK_FUNCS_DEFINED=1
fi

# Function for setting console font if required
set_consolefont() {
    [[ $CONSOLEFONT ]] || return 0
    stat_busy "Loading Console Font: $CONSOLEFONT"
    #CONSOLEMAP in UTF-8 shouldn't be used
    [[ $CONSOLEMAP && ${LOCALE,,} =~ utf ]] && CONSOLEMAP=""
    for i in /dev/tty[0-9]*; do
	/usr/bin/setfont ${CONSOLEMAP:+-m ${CONSOLEMAP}} \
	    $CONSOLEFONT -C ${i} >/dev/null 2>&1
    done
    if (($? != 0)); then
	stat_fail
    elif [[ $CONSOLEMAP ]]; then
	cat <<"EOF" >>/etc/profile.d/locale.sh
if [ "$CONSOLE" = "" -a "$TERM" = "linux" -a -t 1 ]; then printf "\033(K"; fi

EOF
	stat_done
    fi
}

# Source additional functions at the end to allow overrides
for f in /etc/rc.d/functions.d/*; do
  [[ -e $f ]] && . "$f"
done

# End of file
# vim: set ft=sh sw=2 ts=2 et:
