#!/bin/sh

# /etc/init.d/splashy-init

# Copyright 2005, 2006 Splashy Project http://alioth.debian.org/projects/splashy
# Copyright 2005, 2006 Jacobo Vilella (aka Jacobo221) <jacobo221@gmail.com>
#
# This file is part of Splashy.
#
# Splashy is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation;
# either version 2 of the License, or (at your option) any
# later version.
#
# Splashy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with Splashy; if not, write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA

# init script for the service splashy
# Read details about this script in the Readme.txt file

### BEGIN INIT INFO
# Provides:          splashy
# Required-Start:    
# Should-Start:      mountvirtfs
# Required-Stop:     
# Should-Stop:       
# Default-Start:     S 2 3 4 5
# Default-Stop:      0 2 3 4 5 6
# Short-Description: splashy daemon used for presenting a boot splash for Linux
# Description:       The splashy service is used for starting a user-space boot
#       splash utility that draws an image and a progress bar while the system
#       is booting.
# X-UnitedLinux-Default-Enabled: yes
### END INIT INFO

# COMMENTS:
# - Debug lines easily removable: delete all lines containing "DEBUG" or "debug"
#
# TODO:
# - splashy_pgrep... must go away :)
# - Support fsck
#
# NOT TODO:
# - The script does not support filenames with spaces in /etc/rc?.d/*, but
#   supporting it would add too much mess to the script and no one really
#   has that
# - Determine the scripts that will be run more acuratelly by not including
#   those that will not be started/stopped by an optimized rc(S) script,
#   because:
#   * There is no way to be sure that the optimization will happen
#   * It is too CPU expensive
### END NOTES INFO

SC_NAME="${0##*/}"
NAME="splashy"
DESC="Splashy client"

# Script-only options. Do not touch unless you know what you are doing
CONFIG="/etc/default/splashy"
#CHANGE: Once splashy_config --check works, return code should tell also if the
# file does not exist, thus the $XML_CONFIG variable will be useless
XML_CONFIG="/etc/splashy/config.xml"
SC_SLEEP="0.5"
#CHANGE: Make it into config.xml? Anyway, check in config_parse
CONSOLE="/dev/vcs1"

# LSB strongly recommends not to trust $PATH
# /usr/bin should not be trusted. It might not be mounted at most of the script's
# life. Use with extreme care
export PATH=/bin:/sbin:/usr/bin

### BEGIN MAIN FUNCTIONS
# Load LSB's functions
# Why adding this here instead of only where it is needed? LSB's init-functions
# file could run some inline commands which should be run on all init scripts
if test -r /lib64/lsb/init-functions; then
	. /lib64/lsb/init-functions
else
	# Simulate LSB functions if the real ones are not available
	echo "Please install LSB init-funcitons!"
	log_begin_msg() { echo $@; }
	log_end_msg() { if test $1 -eq 0; then echo done; else echo failed; fi;}
	log_succees_msg() { echo $@; }
	log_warning_msg() { echo $@; }
	log_failure_msg() { echo $@; }
fi

# Provide an interface to be able to silent commands not bothering if
# If -2 is first argument, only stderr is silenced
# If -S is first argument, command will be silenced even when /dev/null doesn't
#  exist and will return true if the command had no output and false otherwise
#  This only applies to the cases when no /dev/null exists
# If -s is first argument, same as when -S, but returns true/false inveresly
# Options -S and -s imply -2
# Since debug() already calls silent() do not use debug in this function
silent() {
	if test "$1" = -2; then
		smode=2
		shift
	elif  test "$1" = -S; then
		smode=S
		shift
	elif  test "$1" = -s; then
		smode=s
		shift
	fi
	if test -c /dev/null; then
		if test "$smode" = 2; then
			"$@" 2>/dev/null
		else
			"$@" >/dev/null 2>&1
		fi
	else
		if test "$smode" = S; then
			"$@" 2>&1 | if read i; then false; else true; fi
		elif test "$smode" = s; then
			"$@" 2>&1 | if read i; then true; else false; fi
		else
			# Cannot silence it
			"$@"
		fi
	fi
}
# Output debug info passed as arguments when $DEBUG is enabled
# Return 0 when debug mode is set. Return 1 otherwise
# When -S is set, it only have effect when DEBUG=2, return 1 otherwise
# Debug is useless before executing config_parse()
debug() {
	debugnull=""
	is_enabled $DEBUG || return 1
	test "$1" = -S && case "$DEBUG" in 2|4) shift ;; *) return 1 ;; esac
	test -w /dev/null || debugnull=" (/dev/null doesn't exist)"
	if test $# -gt 0 && is_enabled "$LOG"; then # debug log
	  	# On boot fs is ro. Buffer until it is writtable # debug log
		# we cannot trust test -w # debug log
		# Also, touching the file makes sure the file exists by the time
		# echo >> $LOG is executed
		if silent -S touch "$LOG"; then # debug log
	  		test -n "$LOGBUFF" && # debug log
				echo -e "$LOGBUFF" >> "$LOG" && # debug log
				unset LOGBUFF # debug log
		  	echo -e "$$: $*$debugnull" >> "$LOG" # debug log
		else # debug log
			LOGBUFF="$(echo $LOGBUFF"\n"$$: $*$debugnull)" # debug log
		fi # debug log
	fi; test $DEBUG != 3 -a $DEBUG != 4 && test $# -gt 0 &&
		echo -e "=Splashy debug ($$)=: $*$debugnull"; return 0; }

# Check if $1 is a number
# It requires another number as $2 to check this. Some number which $1 can
# never be
is_number() {
	silent -S test "$1" -ne "$2"
}

# Return true when $1 is an 'enable value'
is_enabled() {
	local value
	test $# -lt 1 && return 1
	for value in "" 0 no No NO disable Disable DISABLE false False FALSE \
		     disabled Disabled DISABLED; do
		test "$1" = "$value" && return 1
	done
	return 0
}

# This function shows the error message <msg> and exists with code <code>
# By default, code is 1
# error <msg> [code]
# error <code>
error() {
	is_enabled "$initmsg_call" && log_end_msg 1
	debug "Error(): $2"
	if test $# -gt 1; then
		if test $2 -eq 0; then
			log_warning_msg "$1"
		else
			log_failure_msg "$1"
		fi
		exit $2
	else
		log_failure_msg "$1"
		exit 1
	fi
}

# This function just checks if, given a path patter, at least one file exists
# matching that pattern
# This is useful because ls might file in many circumstances (for example, with
# deep linked symbolic links) and test only allows a single file to be checked
file_exists() {
	local file
	for file in $1; do
		test -e "$file" && return 0
	done
	return 1
}

#CHANGE: delete once --check is implemented in /sbin/splashy_config
splashy_config() {
	case "$1" in
	--check)
		if test -r "$XML_CONFIG"; then
			return 0
		else
			return 1
		fi
		;;
	*)
		/sbin/splashy_config "$@"
		;;
	esac
}

is_enabled "$EXTERN_PROGRESS" ||
# See if the current theme in the current runlevel must have a progress bar
# The result is returned in $SET_PROGRESS and the return value is that of
# the splashy_config command
get_set_progress() {
	if test "$RUNLEVEL" = 0 -o "$RUNLEVEL" = 6; then
		SET_PROGRESS="$("$CONFIG_READER" --get-key \
		  /splashy/progressbaronshutdown)"
	else
		SET_PROGRESS="$("$CONFIG_READER" --get-key \
		  /splashy/progressbaronboot)"
	fi
}

# Load options
# Export them so that subshells can read them too
config_parse() {
	# Unset all variables so that in a USR2 signal, the are all rechecked
	# Do not unset FRAMEBUFFER, RUNLEVEL nor PREVLEVEL since they could be
	# taken from environment variable and the script would loose their value
	# Do not unset DEBUG either since some effects are unrevertable once
	# the scripts started, such as server startup when DEBUG=2
	unset ENABLE SERVER UPDATE_CMD CONFIG_READER SLEEP_TIME CHVT_TTY \
	      PIDFILE CLIENT_PIDFILE END_MATCHES MATCH SET_PROGRESS \
	      EXTERN_PROGRESS POST_EXEC_SERVICES ENABLE_INITRAMFS
#	      INVERT_DOWN FIFO_FILE
	# Unset $prctg in case after a USR2 signal there has to be no progress
	unset prctg
	# If the script is run manually, get the current runlevel or exit
	# Another chance that $RUNLEVEL might not be defined is in old
	# init binnaries, but anyway splashy will only work on 2.6 linux
	# kernels, which should be installed along with newer init versions
	if test -z "$RUNLEVEL"; then
		RUNLEVEL="$(silent -2 runlevel)"
		test "$RUNLEVEL" = unknown &&
			error "Splashy: No priviledges to determine runlevel"
		# Use dd instead of cut since cut is in /usr/bin. dd spits
		# stats. attempt to filter them. No matter if that filtering
		# generates an error: return code is not checked and dd will
		# spit messages if /dev/null doesn't exist anyway
		RUNLEVEL="$(echo "$RUNLEVEL" |
		            dd skip=2 count=1 bs=1 2>/dev/null)"
		test -z "$RUNLEVEL" &&
			error "Splashy: No priviledges to determine runlevel"
	fi
	if test -z "$PREVLEVEL"; then
		# See comment above about why using dd here
		PREVLEVEL="$(silent -2 runlevel |
		             dd count=1 bs=1 2>/dev/null)"
		test -z "$PREVLEVEL" && PREVLEVEL=N
	fi
	# Defaults which can be overwritten by the config file
	POST_EXEC_SERVICES="keymap.sh console-screen.sh"
	LOG="/var/tmp/splashy-init.log"
	# Do not include "halt reboot" since they should hold the image
	END_MATCHES="^[Ss]top[Ss]plashy$ ^[Ss]plashy[Ss]top$ ^gdm$ ^kdm$ ^wdm$
	  ^xdm$ ^3xdm$ ^startx$"
	# Load script configuration file
	if test -r "$CONFIG"; then
		. $CONFIG
	else
		log_warning_msg "Splashy: Missing $CONFIG. Using defaults"
	fi
	# By LOGBUFF=" " we make sure different debug logs are separated
	case "$RUNLEVEL" in S|0|6) LOGBUFF=" " ;; esac # debug
	# Export DEBUG and LOGBUFF so debug() can be used from now on
	export DEBUG LOGBUFF     # LOGBUFF must be exported so daemon gets the
                                 # debug log from Start
	debug "Config parse: Config reader execution"
	# Load XML configuration file
	test -z "$CONFIG_READER" && CONFIG_READER="/sbin/splashy_config"
	test -x "$CONFIG_READER" ||
		error "Splashy: $CONFIG_READER cannot be executed"
	CONFIG_READER="${CONFIG_READER##*/}" #CHANGE: delete
	"$CONFIG_READER" --check ||
		error "Splashy: $XML_CONFIG config file is broken"
#	FIFO_FILE="$("$CONFIG_READER" --get-key /splashy/fifo)"
	PIDFILE="$("$CONFIG_READER" --get-key /splashy/pid)"
	get_set_progress || SET_PROGRESS=yes
	debug "Config parse: Reading vars"
	# Default user options
	test -z "$ENABLE" && ENABLE=1
	test -z "$SERVER" && SERVER="/sbin/splashy"
	test -z "$UPDATE_CMD" && UPDATE_CMD="/sbin/splashy_update"
	test -x "$UPDATE_CMD" || error "Splashy: $UPDATE_CMD cannot be executed"
	test -z "$FRAMEBUFFER" && FRAMEBUFFER="/dev/fb0"
	test -z "$SLEEP_TIME" && SLEEP_TIME=1
#	test -z "$INVERT_DOWN" && INVERT_DOWN=0
#	INVERT_DOWN=0 # Disable always since it is not usable (on server side)
	# Support old variable names
#	test -n "$spl_fifo" -a -z "$FIFO_FILE" && FIFO_FILE="$spl_fifo"
	test -n "$spl_tty" -a -z "$CHVT_TTY" && CHVT_TTY="$spl_tty"
	# Check configuration values
	test -x "$SERVER" || error "Splashy: $SERVER cannot be executed"
	# Translate the END_MATCHES to the strings that will be used
	# Note that $END_MATCHES applies only for start scripts
	# If it is a number, just detect level. But make sure it is _just_ a
	# number, with no spaces
	if echo "$END_MATCHES" | grep '^[0-9]\+$' -q &&
	  test $END_MATCHES -lt 100; then
		debug "MATCH is a number"
		MATCH="S$END_MATCHES*"
	else
		debug "MATCH is a key"
		for imatch in $END_MATCHES; do
			test -z "$imatch" && continue
			if test -n "${imatch#*\$}"; then
		    		imatch="$imatch*"
			else
		    		imatch="${imatch%\$}"
			fi
			if test -n "${imatch%%^*}"; then
		    		MATCH="$MATCH *$imatch"
			else
		    		MATCH="$MATCH S[0-9][0-9]${imatch#^}"
			fi
		done
		debug "MATCH: $MATCH"
	fi
	# Do not check $SLEEP_TIME with is_number(): It could be a decimal
	echo "$SLEEP_TIME" | grep -E '^[0-9]+\.*[0-9]*$|[0-9]*\.*[0-9]+' -q ||
		error "Splashy: Wrong SLEEP_TIME value $SLEEP_TIME"
	# Check $CHVT_TTY value
	if test -z "$CHVT_TTY"; then
		CHVT_TTY=auto
	elif ! is_number "$CHVT_TTY" -1 &&
	  test "$CHVT_TTY" != auto -a "$CHVT_TTY" != none; then
		error "Splashy: Invalid CHVT_TTY value ($CHVT_TTY)"
	fi
	debug && test "${LOG%/*}" != "$LOG" -a ! -w "${LOG%/*}" &&
		error "Splashy: Cannot log debug to $LOG"
	# Get other settings
	CLIENT_PIDFILE="$PIDFILE.client"
	# Don't check the existence of the PID file. It might not
	# yet exist, but check that it is set
#	# Same goes for the FIFO file
#	test -z "$FIFO_FILE" &&
#		error "Splashy: No fifo file set in $XML_CONFIG"
	test -z "$PIDFILE" && error "Splashy: No pid file set in $XML_CONFIG"
	# Everything was successfull, so export them and set $CONFIG_PARSED
	# No need to export $END_MATCHES since it is replaced with $MATCH
	export ENABLE SERVER UPDATE_CMD CONFIG_READER SLEEP_TIME CHVT_TTY \
	       PIDFILE CLIENT_PIDFILE MATCH FRAMEBUFFER EXTERN_PROGRESS \
	       SET_PROGRESS RUNLEVEL PREVLEVEL LOG POST_EXEC_SERVICES \
	       ENABLE_INITRAMFS CONFIG_PARSED=yes
#	       INVERT_DOWN FIFO_FILE
	debug "Config parse: Done"
}

# Detect if this script is already running
# Arguments correspond to PIDs that will be ignored
# Must be called _after_ calling config_parse()
# It returns the PID(s) in variable $pid if successfull
# Beware it could return duplicate PIDs
is_client_running() {
	debug "Client_run(): Parent script PIDs: $SCRIPTSPIDS"
	# The sorting of the matches is from least CPU heavy to most heavy
	if test -e "$CLIENT_PIDFILE"; then
		pid=`cat "$CLIENT_PIDFILE"`
		silent -S kill -0 $pid &&
		# Make sure that it is actually splashy. On init scripts, it
		# is very easy for another process to have the same pid that
		# a splashy process on an earlier boot
		grep -i splashy -q /proc/$pid/cmdline &&
			return 0
	fi
	local file files ipid npid tpid
	for file in "$0" /etc/rc?.d/[SK][0-9][0-9]splashy* \
	  /etc/init.d/*splashy*; do
		files="$files $file"
	done
	pid="$(pidof -x $files)"
	# Do not use pidof's -o option. It is broken: it will fail
	# to work when the results have duplicate pids and it only
	# accepts 4 -o pids
	# Therefore, here is an independent and good implementation
	for ipid in $pid; do
		for npid in $SCRIPTSPIDS; do
			test $npid -eq $ipid && continue 2
		done
		tpid="$tpid $ipid"
	done
	test -n "$tpid" && pid="$tpid"
}

# Detect if the splashy server is already running
# Must be called _after_ calling config_parse()
# It returns the PID in variable $pid if successfull. Be aware that more than
# one PID could be returned in $pid
# It uses $SERVER_PID to remember the pid (and check it) across calls
is_server_running() {
	# The sorting of the matches is from least CPU heavy to most heavy
	if test -n "$SERVER_PID"; then
		silent -S kill -0 $SERVER_PID &&
			pid="$SERVER_PID" && return 0
	fi
	debug "is_server(): var pid failed"
	if test -e "$PIDFILE"; then
		pid=`cat "$PIDFILE"`
		silent -S kill -0 $pid &&
			# Update SERVER_PID so next time it is faster to check
			SERVER_PID="$pid" && return 0
	fi
	debug "is_server(): pid file failed"
	pid="$(pidof "${SERVER##*/}")" &&
		# Update SERVER_PID so next time it is faster to check
		SERVER_PID="$pid" && return 0
	unset pid SERVER_PID
	debug "is_server(): server not running"
	return 1
}

# Remove the PID file and close splashy server before exitting
# This does not close the server, since that would run into a race condition
# (clean_exit calls send_command which calls clean_exit...)
clean_exit() {
	is_enabled "$EXTERN_PROGRESS" ||
		silent -S rm -f "$CLIENT_PIDFILE"
	debug "Clean_exit(): $1"
	exit $1
}

is_enabled "$EXTERN_PROGRESS" ||
# Sends commands to Splashy
# In case of error, exit
send_command() {
	debug "send_command(): Called as $*"
#	test ! -p "$FIFO_FILE" &&
#		error "Splashy: FIFO file $FIFO_FILE no longer exists!"
	if is_server_running; then
		debug "send_command(): Sending '$*'"
#		# Do not run in the background! This will wait until
#		# splashy-server reads the command, so no bottle-necks
#		# will happen. Also, if it is run while udev is
#		# remounting /dev, running in background would fail
#		# since /dev/null might not exist (and '&' needs
#		# /dev/null)
#		echo $* > "$FIFO_FILE" ||
#			error "Splashy: Unable to send '$*' to Splashy"
		# Luis Mondesi <lemsx1@gmail.com>
		# We use splashy_update to send our commands to Splashy's fifo
		# This command allows us to write to the FIFO without blocking
		# this script (non-io block). splashy_update will report
		# error codes according to the error that was encountered. If
		# we encounter a recoverable error, we try at least 10 times
		# before we give up on the command
		for i in 1 2 3 4 5 6 7 8 9 0; do
			upout="$("$UPDATE_CMD" "$*" 2>&1)"
			upcode="$?"
			debug "splashy_update code: $upcode"
			test -n "$upout" && debug "splashy_update out: $upout"
			case "$upcode" in
			0)
				debug "Command sent"
				break
				;;
			1|4)
				debug "Sleeping  0.5s"
				sleep 0.5
				;;
			2)
				debug "This error is ignored"
				break
				;;
			3)
				debug "Update_cmd: args failure!"
				break
				;;
			esac
		done
	else
		# The server is not running anymore (user closed it)
		debug "send_command(): No server"
		debug -S ||
		clean_exit 0
	fi
}

is_enabled "$EXTERN_PROGRESS" ||
# This is a hack I've come up with to see if X is running
# since there is no 'official way' of getting this information
# If there is an X running, provide the tty number of the first X screen
# It returns the result in $X_tty. If no X or no valid result is found,
# $X_tty is either unset or set to "" and return code is 1. Otherwise, return
# code is 0
get_X_tty() {
	debug "get_X(): Running"
	local f X_sc X_sc_lowest X_pid
	# See if an X is running and, if so, get the lockfile of the
	# lowest X screen
	for f in /tmp/.X*-lock; do
		X_sc="$(echo ${f##*/} | sed 's/\.X\([0-9]*\)-lock/\1/')"
		# If the $X_sc's value is the filename, then the
		# pattern wasn't found
		test "${f##*/}" = "$X_sc" && unset X_sc &&
			continue
		# If $X_sc_lowest isn't yet set, just set it and
		# continue (do not compare)
		test -z "$X_sc_lowest" && X_sc_lowest=$X_sc &&
			continue
		test $X_sc -lt $X_sc_lowest && X_sc_lowest=$X_sc
	done
	# If an X was found running get it's PID
	if test $X_sc_lowest; then
		debug "get_X(): Xnum: $X_sc_lowest"
		X_pid=`sed 's/[^0-9]*\([0-9]*\).*/\1/' \
	  	/tmp/.X"$X_sc_lowest"-lock`
		# Make sure we are getting a number. Comparing a pid
		# against 0 is a safe check
		if is_number "$X_pid" 0; then
			# This is the most precise way, but requires
			# root priviledge
			if test -r /proc/$X_pid/fd; then
				debug "get_X: Root"
				for f in /proc/$X_pid/fd/*; do
					# Search for a /dev/tty* file
					# being used by the X server
					X_tty="$(readlink "$f" |
						grep "^/dev/tty")" &&
							break
				done
				# See if a device was found. If so, get
				# its number
				if test -n "$X_tty"; then
					X_tty="$(echo "$X_tty" |
						sed 's/[^0-9]*\([0-9]*\)/\1/')"
					# Make sure we are getting a
					# number. Comparing a tty
					# against a nevative number is a
					# safe check
					is_number "$X_tty" -1 ||
						unset X_tty
				fi
			fi
			# This method similar to `ps -aux | grep X |
			# grep vt7`, but more precise (though more CPU
			# expensive)
			# It should be available for any user, even
			# non-priviledged
			# Priviledged uers may also come here if the
			# other method failed
			if test -z "$X_tty"; then
				debug "get_X(): NoRoot"
				# The reason for using "echo `cat ...` |
				# tr ... | sed ...", instead of passing
				# the file directly as an argument is
				# that sed doesn't hande files with NULL
				# characters, and cmdline has some
				X_tty="$(cat /proc/$X_pid/cmdline |
					tr '\000' ' ' |
					sed 's/.*\ vt\([0-9]*\)[\ \x0$].*/\1/')"
				# Make sure we are getting a number
				# Comparing a tty against a nevative
				# number is a safe check
				is_number "$X_tty" -1 || unset X_tty
			fi
		fi
	fi
	debug "get_X(): X_tty: $X_tty"
	test -n "$X_tty" || return 1
}

is_enabled "$EXTERN_PROGRESS" ||
# Complete the progress bar
# $prctg is global in case a USR2 signal happened
perform_complete() {
	debug "Complete: $atempts=$succeeded+$repeats/$failed"
	# The progress has been completed. Complete progress and exit
	if is_enabled "$SET_PROGRESS"; then
#		if test "$RUNLEVEL" = 6 -o "$RUNLEVEL" = 0 &&
#		   is_enabled "$INVERT_DOWN"; then
#			prctg=0
#		else
		prctg=100
#		fi
		# Do not update progress if it is already set to that percentage
		if test "$oldprctg" != "$prctg"; then
#			debug "Complete: Sending 100%: $prctg - $oldprctg"
#			send_command "progress $(($prctg - $oldprctg))"
#			debug && i=$(($prctg - $oldprctg))
#			debug "FINAL INCREMENT: $inc_count + $i"
			debug "Complete: Sending 100%: $prctg"
			send_command "progress $prctg"
		fi
	fi

	# Switch to X's terminal when not shutting down
	if test "$RUNLEVEL" != 6 -a "$RUNLEVEL" != 0; then
		# If a tty was set to switch in config, switch to it
		# Otherwise, try to determine where X is running, or
		# don't switch
		if test "$CHVT_TTY" = auto; then
			debug "Get tty: yes"
			if get_X_tty; then
				switch_tty="$X_tty"
			else
				debug "Get tty: no. Defaulting to 1"
				switch_tty=1 # Fallback to tty1
			fi
		else
			switch_tty="$CHVT_TTY"
		fi
		debug "Switch X initialized"
		# If a tty is set, switch to it
		# Don't use 'chvt' command: It is not a base distro command
                # use splashy_chvt by symlink splashy -> splashy_chvt
                # or send a command to splashy via fifo: chvt $TTY_NUMBER
		if [ "$switch_tty" != none ]; then
			debug "Switch to $switch_tty tty"
			# Allow Splashy to change vt
			#send_command "allowchvt"
			# Tell Splashy to switch vt
			#send_command "chvt $switch_tty"
		fi
		debug "Server: Exitting"
		send_command "exit"
		while is_server_running; do sleep 0.1; done
                splashy_chvt $switch_tty
	fi
	# If Splashy is supposed to be running from initramfs, so any init
	# script running unicode{start|stop} will be patched and must be rerun
	# now Do not use "is_enabled $ENABLE_INITRAMFS" but instead check if it
	# is enabled using the same check used in initramfs/hooks/splashy
	if test "$ENABLE_INITRAMFS" = 1 || \
            silent grep "splashy" -q -m 1 "/etc/init.d/console-screen.sh"; then
		debug "Initramfs enabled"
		# Timeout after 10 seconds
		for i in 1 2 3 4 5 6 7 8 9 0; do
			# Do the same check that the init scripts do when patched
			silent pidof splashy || break
		done
		debug "Server died"
		# Even if splashy is still running, start the services. In the
		# worst case it will halt the system until splashy server
		# timesout which is what would have happened anyway if this code
		# wasn't here
		# Execute the services in the same order they should
		if test "$POST_EXEC_SERVICES" = auto; then
			debug "Autodetecting services to rerun"
			if test "PREVLEVEL" = N; then
				runlevels="S $RUNLEVEL"
			else
				runlevels="$RUNLEVEL"
			fi
			for rl in runlevels; do
				for file in \
		 	  	`grep -i unicode_ /etc/rc"$rl"/* -m1 | \
                                  	cut -f1 -d:`; do
					debug "Rerunning $file"
					test -x "$file" && "$file"
				done
			done
		else
			for file in $POST_EXEC_SERVICES; do
				debug "Rerunning service $file"
				test -r "/etc/init.d/$file" || continue
				case "$file" in
				*.sh)	sh "/etc/init.d/$file" start
					debug "Shell service rerun successfully"
					;;
				*)	test -x "/etc/init.d/$file" || continue
					/etc/init.d/"$file" start
					debug "Binary service rerun successfully"
					;;
				esac
			done
		fi
	fi
	debug "Client: Exitting"
	clean_exit 0
}

is_enabled "$EXTERN_PROGRESS" ||
# Get the aproximate list of scripts that will be run for the passed
# as argument runlevel
# The list of scripts is appended to $scripts and the amount of scripts
# is summed to $nscripts
get_scripts() {
	local file
	# Ignore K scripts in first runlevel since boot
	# Also note that $MATCH applies only for start scripts
	# This is because a runlevel might have a name twice (as a K and an S
	# script) and it would make no sense to stop splashy at K level. Same
	# would happen in name collision between runlevels S and 2 for example
	if test "$PREVLEVEL" != N; then
		# Make sure that at least such a file exists, otherwise we
		# would get the exact literal string
		file_exists "/etc/rc$1.d/K[0-9][0-9]*" &&
			for file in /etc/rc"$1".d/K[0-9][0-9]*; do
				scripts="$scripts$1${file##*/} "
				nscripts=$(($nscripts + 1))
			done
	fi
	# Make sure that at least such a file exists, otherwise we
	# would get the exact literal string
	file_exists "/etc/rc$1.d/S[0-9][0-9]*" &&
	  if test "$1" != S -a "$1" != s; then
		local done oldlevel count var newlevel
		for file in /etc/rc"$1".d/S[0-9][0-9]*; do
			file="${file##*/}"
			newlevel="$(printf "%.3s" "$file")"
			scripts="$scripts$1$file "
			if test -z "$done"; then
				if test "$oldlevel" = $newlevel; then
					count=$(($count + 1))
				else
					oldlevel=$newlevel
					count=0
				fi
				for match in $MATCH; do
					case "$file" in
					$match)
						done=1
						var=$(($var - $count))
						debug "Get_scripts(): M: $file"
						break
						;;
					esac
				done
				# Even when done, +1, because when a matching
				# script is reached, it is that script that
				# ends, not the one right before it
				# Notice that this is optimized for parallel
				# booting scripts. It will not be all that good
				# in serial scripts booting, since all $oldlevel
				# scripts are disconted and then +1, so if the
				# matched script wasn't the first one, it might
				# stop before it is reached
				var=$(($var + 1))
			fi
		done
		nscripts=$(($nscripts + $var))
	else
		for file in /etc/rc"$1".d/S[0-9][0-9]*; do
			scripts="$scripts$1${file##*/} "
			nscripts=$(($nscripts + 1))
		done
	fi
}

is_enabled "$EXTERN_PROGRESS" ||
# Get the aproximate list of scripts that will be run in the given runlevel
# This wrapper simple adds the ability to include post-S scripts
# Notice that this will never be exact since rc(S) scripts tend to optimize
# performance by not stopping or running those services which are kept or
# or not restarted along runlevels
# The script names are returned along with the runlevel in $scripts
# The amount of scripts returned is returned in $nscripts
# It sets $target_rl when in booting S runlevel and the next runlevel is known
# It uses $target_rl_was_set to remember if $target_rl has already been set
get_scripts_wrapper() {
	unset scripts nscripts
	get_scripts "$1"
	debug "Wrapper(): $1 = $nscripts"
	# Export them now since the function might be left due to
	# errors at any moment. They must be exported to be usable
	# across shells
	export scripts nscripts target_rl
	if test "$1" = S -o "$1" = s; then
		# Notice that /proc could not yet be mounted
		test ! -e /proc/1 && return
		# Only set it the first time we enter runlevel S
		if ! is_enabled "$target_rl_was_set" &&
		   test "$PREVLEVEL" = N; then
			target_rl="$(sed 's/init\ \[\([Ss0-9AaBbCc]\)\].*/\1/' \
			  /proc/1/cmdline)"
			test "$target_rl" = "$(cat /proc/1/cmdline)" -o \
			  "$target_rl" = S -o "$target_rl" = s -o \
			  -z "$target_rl" &&
				unset target_rl && return
			get_scripts "$target_rl"
			debug "Wrapper(): S + $target_rl = $nscripts"
		fi
	fi
}

# Start the splashy server accordingly to the runlevel we are in or going to
# It accepts "-s" as its only argument. When passed, it will not show errors nor
# exit if failure happens
# $vga will never vary during a system session so it is safe keeping it through
# USR2 signals and overwritting it
# Returns true on success
start_server() {
	local server_code=1 command
	if test "$RUNLEVEL" = 0 -o "$RUNLEVEL" = 6; then
		debug "Server(): Run: shutdown"
		command=shutdown
	else
		debug "Server(): Run: boot"
		command=boot
	fi
#	# When debug option is enabled in splashy server, uncomment line below
#	debug && command="--debug $command"
	# We cannot rely on splashy-server writting the pid, since the
	# filesystem might not be writable yet
	# Notice that splashy server is silenced, so all errors must be handled
	# by the script
	
        ! debug -S &&
	if silent "$SERVER" $command &&
	  # Make sure there is only one instance running
	  is_server_running && is_number "$pid" 0; then
#	        # With incremental progress commands it is not possible to
#		# restore the progress bar
	  	server_code=0
		# Try to restore previous progress bar status if there was
		if test -n "$prctg"; then
			send_command "progress $prctg" || server_code=1
		fi
	else
		server_code=1
	fi
        debug -S && server_code=0
	# See if the server was successfuly initiated
	if test $server_code -ne 0 -a "$1" != "-s"; then
		# Let's now determine the exact reason for the failure and tell
		# it
		echo "Splashy failed because of the following reason(s):"
		test ! -e /proc/fb &&
			echo " - No framebuffer kernel support is available"
		! test -w "$FRAMEBUFFER" &&
			echo " - Framebuffer device unwrittable ($FRAMEBUFFER)"
#		# rc scripts get kernel foo=bar arguments as environment
#		# variables. So check if vga= is set (this is an alternate way
#		# of checking /proc/cmdline, since /proc could not yet be
#		# mounted) or try to find it out
#		# Grub method
#		test -z "$vga" &&
#		  # Lilo (kernel docs) method
#		  vga="$(silent -2 dd count=1 bs=2 skip=295165 if=/dev/mem |
#		    silent -2 od -d - | head -n 1 | tr -s \  | cut -f 2 -d \ )"
#		! silent -S test "$vga" -ge 769 -a "$vga" -le 799 &&
#		  # Desperate detection
#		  # It is practically impossible to detect all possible values,
#		  # just run and take a look at:
#		  # egrep '(fix(\.|->)id.*\"|\.id[[:space:]]*=[[:space:]]*\")' \
#		  #   -R /usr/src/linux/drivers/video/ | \
#		  #   sed 's/.*\"\([^\"]*\)\".*/\1/'
#		  # ...and that's just some (most) of them
#		  ! silent -S grep 'VESA VGA' /proc/fb -q &&
#			echo " - Kernel parameter vga= not set"
		# To see if Framebuffer is usable just check that /proc/fb
		# contains data (framebuffer's identification string)
		# If it does, it is usable (/proc/fb never contains error
		# messages, etc), if it doesn't, it is not (all framebuffer
		# devices _must_ define a 'char id[16]' string
		test -z "$(cat /proc/fb)" &&
			echo " - Framebuffer disabled. Set vga= value in kernel"
		is_server_running && 
		  if ! is_number "$pid" 0; then
			echo " - There is more than one Splashy server running!"
		  else
		  	echo " - Another Splashy server is already running"
		  fi
		test ! -p "$("$CONFIG_READER" --get-key /splashy/fifo)" &&
		  	echo " - FIFO file doesn't exist"
		debug || # Do not fail on debug mode. Keep going
		error "Failed to start Splashy server"
	fi
	return $server_code
}
### END MAIN FUNCTIONS

### BEGIN COMMON STUFF
# Add this pid to the list of PIDS is_client_running() should ignore and export
# it so it is readable from subshells
export SCRIPTSPIDS="$SCRIPTSPIDS $$"

# Parse the config files. Only if not parsed already
is_enabled "$CONFIG_PARSED" || config_parse

debug "Starting client: $0 ($SC_NAME)"
debug "Runlevel: $RUNLEVEL, Prev: $PREVLEVEL"

# This is the only way possible to have it behave appropiately in some
# circumstances, although breaking LSB
case "$SC_NAME" in
[KS][0-9][0-9][Ss]top[Ss]plashy | [KS][0-9][0-9][Ss]plashy[Ss]top)
	# The capital 'S' is on purpose to know it is an init script
	mode=Stop
	;;
[KS][0-9][0-9]*)
	if ! is_enabled "$START_RUN" && test "$1" = start -o "$1" = stop; then
		# The capital 'S' is on purpose to know it is an init script
		mode=Start
	else
		mode="$1"
	fi
	;;
*)	mode="$1"
	;;
esac

# This script should be run in start mode from a K call in all runlevels but S.
# If this is not the case, assume it is already running from a K script. It is
# necessary to run the script in all runlevels as both K and S script because
# of the reason explained in the README file under section "USER NOTES"
# Also, remove the splashy server pid file when booting the system
test "$mode" = Start && test "$RUNLEVEL" = S -o "$RUNLEVEL" = s &&
	case "$SC_NAME" in
	K*)	error "Please do not run Splashy as a K script from runlevel S"
		;;
	S*)	silent -S rm "$PIDFILE"
		;;
	esac

debug "Mode: $mode. $$"
### END COMMON STUFF

### BEGIN ACTIONS
# Behave appropiately to the command
case "$mode" in
# The start mode does initial checks before running the daemon and sets
# everything up so that the daemon can run in background as lightly as
# possible
[Ss]tart)
	# Do not run on RUNLEVEL 1. Don't bother disabling this:
	# The splashy-server checks this too and will not run on runlevel 1
	test "$RUNLEVEL" = 1 && error "Splashy will not run in runlevel 1" 0
	# We do not want to run splashy when switching from runlevel S to the
	# next runlevel: The user might have pressed ESC and will not want it to
	# pop up again
	test "$PREVLEVEL" = N -a "$RUNLEVEL" != S -a "$RUNLEVEL" != s &&
		debug "Do not enter, already should have been run" &&
		exit 0
	# We do not want to run splashy when switching from one runlevel to the
	# next runlevel (i.e.: from 2 to 3): The user might have pressed ESC and
	# will not want it to pop up again. The only case in which we don't have
	# to mind about this is in runlevels 0 and 6, since there is no next
	# runlevel.
	# To check this, just see if the script is being called as S*, if it is,
	# unless it is runlevel S, exit, since it should have already been called
	# as a K* script
	case "$RUNLEVEL" in
	0|6|S|s) ;;
	*)
		case "$SC_NAME" in
		S*)
			debug "Should be already running from a K* script"
			exit 0
			;;
		K*) debug ;;
		*) debug "Amazing BUG around!" ;;
		esac
	esac
	# We are using the initrc messages here instead of using them in the
	# daemon (where they should be) because the init-functions currently
	# do not support parallel running
	# log_begin_msg is available since LSB 2.0, while log_daemon_msg is new
	# in LSB 3.0. But we will simulate log_daemon_msg through log_begin_msg
	log_begin_msg "Starting $DESC: $NAME"
	# Under certain circumstances (such as when exitting) it is necessary
	# to know if the log_begin_msg function was called and is waiting
	# for a log_end_msg call
	initmsg_call=yes
	# Since init scripts are config files, they might be left after an
	# uninstall. Check whether the binary is still present:
	test ! -x "$SERVER" &&
		error "Splashy: $SERVER not installed. Purge Splashy" 5
	# Do not start if disabled and called as init script
	! is_enabled "$ENABLE" && test "$mode" = Start &&
		error "Splashy is disabled in $CONFIG" 0
	# Do not run if this script is already running
	# This must obviously be checked before the pid file is rewritten
	# Return successful as described in LSB
	is_client_running &&
		# This script is run twice during almost all runlevels to avoid
		# the rc script optimizations (see USER NOTES in the Readme.txt
		# file), so try to give an apropiate output for each case
		if test $mode = Start; then
			debug "Splashy client already running"
			echo " status is \"running\"."
			exit 0
		else
			error "Splashy: Already running" 0
		fi
	# Perform some operations with the script name that may be necessary
	# later
	test $mode = Start && export NAME_FULL="$0"
	debug "Start: NameFull: $NAME_FULL"
	# Get the scripts here so it is done when entering daemon mode
	is_enabled "$EXTERN_PROGRESS" ||
	get_scripts_wrapper "$RUNLEVEL"
	# Be able to know if the script was run with start initially
	export START_RUN=yes
	# Try to run the server, if possible. If it fails, just ignore
	# This will make splashy appear from the very first moment, with no
	# scrolling text at all (almost). Thus the importance of suggestion to
	# be launched after mountvirtfs
	if test -e /proc/1 -a -e /dev/fb0; then
		#CHANGE: Do not check for /dev/vcs1. Splashy could just be
		# showing a static image while on initramfs
		! is_server_running &&
		  if test "$PREVLEVEL" = N; then
			# If the system is booting, just try to start it, but
			# don't care if it fails: it will be started again later
			#CHANGE: When the server handles its launching errors
			# correctly, remove this check and launch without -s
			# Otherwise # it might be correctly launched twice (if
			# user entered verbose)
#			# Luis Mondesi <lemsx1@gmail.com>
#			# When using initramfs, splashy will be running with no	
#			# ability to read our named pipe (fifo). We must kill it
#			# first and then start it properly
#			debug "PidOf says $(pidof -s splashy)."
#			silent -S kill -9 $(pidof -s splashy)
#			sleep 1
			start_server -s
		  else
			start_server
		  fi
		test $? -eq 0 && debug "Client: Started server"
		# We cannot do "test $? -eq 0" here to check if the server was
		# started here because splashy could be running from initramfs
		# Anyway, running is_server_running when it was started from
		# this script take sno CPU since the PID is already in the var
		# so it will take justa kill -0 command
		if is_server_running; then
			debug "Client: Server is running"
			export SERVER_RUNNING=y &&
			# If in extern_progress mode, splashy server started,
			# so exit
			is_enabled "$EXTERN_PROGRESS" &&
				clean_exit 0
		else debug "Start: Server not running"
		fi
	fi
	# Start in background
	# We assume that /dev/null exists, since this part should be run
	# asynchronously
	debug "Calling daemon mode"
	"$0" daemon &
	# If we got here, the process was successfull as far as we can report
	# up to the moment
	log_end_msg 0
	# This isn't necessary. It is just in case someday something is added
	# below, so we don't forget
	unset initmsg_call
	;;
# NEVER call this script in "daemon" mode directly! Let "start" call it
daemon)
	is_enabled "$START_RUN" || error "Daemon mode cannot be called directly"
	debug "Entering daemon mode"
	### BEGIN FUNCTIONS EXCLUSIVE FOR THIS MODE
	is_enabled "$EXTERN_PROGRESS" ||
	# Set the pid file, if possible
	# It uses $pidmark_done to know if the pid file has been successfully
	# and definitely written
	set_pid_file() {
		# Don't write PID when shutting down, otherwise it would be
		# there when booting back again
		test -n "$pidmark_done" -o \
		  "$RUNLEVEL" = 0 -o "$RUNLEVEL" = 6 &&
		  	return
		# We cannot trust in /dev/null existing
		if test -e /dev/null; then
			( echo $$ > "$CLIENT_PIDFILE" ) 2>/dev/null
		else
			echo $$ > "$CLIENT_PIDFILE"
		fi &&
			# We cannot assume that the filesystem is correctly set
			# and that it will not be remounted until runlevel S
			# has been passed
			# Notice that the order in the check below does matter
			# This function is always called after
			# progress2percentage() so it is safe to assume that if
			# $target_rl is blank, this is not first runlevel S
		   	if test -z "$target_rl"; then
				pidmark_done=yes
				debug "PID(): done"
			else	debug "PID(): written"
			fi
	}

	is_enabled "$EXTERN_PROGRESS" ||
	# Get the $level (s, S or 0-9), $mode (S or K), $progress (rc
	# step) and $name (script name) currently running. Also $script is
	# the whole script's name plus it's runlevel
	# Note that the variables' values may be empty when the progress status
	# cannot be detected
	# Return true when the progress is complete. Otherwise, return false
	# It updates $RUNLEVEL acording to the runlevel it has detected
	# It uses $oldscript to remember the previous script across calls
	get_status() {
		local line
		unset script
		debug "Get_status(): oldscript: $oldscript"
		debug && atempts="$(($atempts + 1))"
		# There are two characteristics which should be studied:
		# 1. CPU usage: We don't want splashy to harm boot time too
		# much
		# 2. (Most important) Atomic: It is extremely important that
		# the delay between the /proc/*/exe listing and the reading of
		# the links is as short as possible (explained later in point
		# 4 about why can no init script be detected)
		# We can almost assure that this following command is the real
		# core of this script, so making it's CPU usage low, makes the
		# sholw script fast
		# Only search for the newest process since we are only
		# interested in processes newer than this script. If no such
		# process is found, then this script will appear, which
		# we'll ignore
		line="$(splashy_pgrep -f -n -l \
		  "/rc[sS0-9AaBbCc\].d/[KS][0-9][0-9]")" &&
			test "${line%% *}" -ne $$ && script="${line#* }"
		# How to get sourced scripts progress too?
		# TheBonsai: "redefine "source" ["."] as a function" "read
		# INVOCATION section in [bash] manpage" "i mean the ENV hack"
		# Jacobo221: "reading kernel docs" "Note that [...] a trailing =
		# on the name of any parameter states that that parameter will
		# be entered as an environment variable"
		# twkm: "it is not exported, it is merely in pid 1's env"
		# (and init passes it to rc/rcS)
		#test -n "$script" && debug "Get_status(): script: $script"
		# It can happen that no rc script is detected to be running in
		# the following cases:
		# 1. /etc/boot.rc/ scripts (deprecated, but still supported) are
		# being run
		# 2. All rc scripts have been run. The stop script should have
		# been run by this time
		# 3. The status check was performed while the rc script itself
		# was iterating into another init script
		# 4. The init script that was being run while the /proc/*/exe
		# listing was performed, died before its link was read
		# No init script has been detected to be running
		if test -z "$script"; then
			debug "Get_status(): No script"
			unset progress mode level
			# See if rc scripts are being run still
			# It could happen that it was switching from
			# runlevel S to another runlevel such as 2, so it
			# would not detect any /etc/init.d/rc(S) script
			# To avoid the switching from S to X runlevel bug,
			# see if runlevel command is set. This command
			# reads the utmp file, which is set by init not
			# before switching from runlevel S. Also notice
			# it cannot be trusted much since the utmp could be
			# from a previous session or just not exist yet
			# But, this doesn't save much, since init sets utmp
			# _during_ the runlevel switching process, so the bug
			# is still there, just more difficult to suffer it
			# Notice that if /proc is unmounted _during_ the
			# script, it will complete the progress bar
			if ! silent -s splashy_pgrep -f -o "/etc/init.d/rc" &&
			   case "$(silent -2 runlevel)" in unknown|s|S) false ;;
			   *) true ;; esac; then
				debug "Get_status(): No rc(S)"
				succeeded=$(($succeeded + 1))
				return 0
			else
				if test -z "$oldscript"; then
					# At least, we are at the progress were
					# this script is called from, if this is
					# an init script
					test -n "$NAME_FULL" &&
						script="$NAME_FULL"
					if test -z "$script"; then
						debug "Get_status(): No 1st" &&
							failed="$(($failed +1))"
						return 1
					fi
					debug "Get_status(): script1: $script"
				else
					debug "Get_status(): Not detected" &&
						failed="$(($failed + 1))"
					return 1
				fi
			fi
		fi
		progress=$(echo $script | sed 's/.*\/[KS]\([0-9][0-9]\).*/\1/')
		level=$(echo $script | sed 's/.*\/rc\([sS0-9AaBbCc]\).*/\1/')
		mode=$(echo $script | sed 's/.*\/\([KS]\).*/\1/')
		name="$(echo $script |
		  sed 's/.*\/[KS][0-9][0-9]\([^[:space:]]*\).*/\1/')"
		script="$level$mode$progress$name"
		debug "Get_status(): Progress: $script ($level$mode$progress)"
		# If we are at the same progress that we were before, skip. We
		# avoid writting to the fifo and loosing cpu cycles in
		# framebuffer drawing
		if test "$script" = "$oldscript"; then
			unset progress mode level
			debug "Get_status(): Repeat ($oldscript)" &&
			  repeats="$(($repeats + 1))"
		else
			# Update the $oldscript variable to check against it
			# on next iteration
			oldscript="$script"
		fi
		# Do not check here if the progress is complete
		# If there is no stop script, this script will detect this later
		debug "Get_status(): oldscript2: $oldscript"
		# Update runlevel
		test -n "$level" && RUNLEVEL="$level"
		return 1
	}

	is_enabled "$EXTERN_PROGRESS" ||
	# Convert the progress status into a percentage number to reflect in the
	# progress bar. Leave the result in the $prctg variable
	# Returns true when the progress is complete
	# Unsets $prctg if it couldn't be detected
	# It updates $PREVLEVEL when the detected level has changed
	# It uses $prevcyclerl to remember the runlevel that was used in the
	# last iteration. It also uses $oldprctg to remember the $prctg value
	# in the last call
	# When it detects the $target_rl runlevel has been passed, it unsets the
	# variable
	# It may also set target_rl_was_set when get_scripts_wrapper() failed to
	# set $target_rl
	# Notice $RUNLEVEL and $level are equivalent here (except when $level is
	# not set)
	progress2percent() {
		debug "Progress: Non-complete"
		# Set the $oldprctg now before it is modified
		oldprctg="$prctg"
		# Check if the variables have been set by get_status
		# If the variables are not set that is because the
		# percentage should not be updated
		# If only one variable is set, the others are too
		test -z "$level" && return 1
		local xscript var file
		debug "Progress: Exists"
		debug && succeeded="$(($succeeded + 1))"
		# This is the first iteration
		if test -z "$prevcyclerl"; then
			prevcyclerl="$level"
			# $target_rl_was_set is very often used in this script
			# to know when we have passed the first S runlevel. So
			# since this is the first iteration, if it isn't S, say
			# so
			test "$PREVLEVEL" != N && target_rl_was_set=yes
		fi
		# See if the runlevel changed in the last iteration. If it did,
		# update the variables
		if test "$prevcyclerl" != "$level"; then
			debug "Progress(): Runlevel $prevcyclerl -> $level"
			debug "Progress(): Target Runlevel1: $target_rl"
			debug "Progress(): Target RL set1: $target_rl_was_set"
			# Set $PREVLEVEL. Only set it when this is not a change
			# from initial S to X
			if test -n "$target_rl_was_set" -a -z "$target_rl"; then
				PREVLEVEL="$prevcyclerl"
				debug "Progress(): PREVLEVEL changed"
			# If the runlevel that goes after S has not been
			# detected, we can assume it is this one
			# Also assume it is this one whe, although $target_rl is
			# set, this other runlevel has taken its place
			elif test -z "$target_rl" -a -z "$target_rl_was_set" ||
			  test -n "$target_rl" -a "$target_rl" != "$level"; then
				debug "Progress(): Hard target_rl detection"
				target_rl="$level"
				# get_scripts_wrapper() also sets $scripts and
				# other variables when it detects $target_rl
				# Since this cannot be done here, set it to
				# value 'half' so it is known later
				target_rl_was_set=half
			# Check if we have just finished running the target
			# runlevel, and if so, set things up
			elif test "$prevcyclerl" = "$target_rl"; then
				unset target_rl
			fi
			debug "Progress(): Target Runlevel2: $target_rl"
			debug "Progress(): Target RL set2: $target_rl_was_set"
			# Update the variables for the new runlevel unless they
			# are already set
			if test "$target_rl" != "$level" -o \
			   "$target_rl_was_set" = half; then
				debug "Progress(): Regetting scripts"
				get_scripts_wrapper "$level"
				# Only do this operations when this is not the
				# post S runlevel
				if test "$target_rl_was_set" != half; then
					debug "Progress(): Redoing thingies"
					get_set_progress
					#CHANGE: Restart server (since it
					# does not support downgrading the
					# progress) and set apropiate command
					# (boot, shutdown) and check
					# progressbaron*. This should be removed
					# when it does
					send_command "exit"
					start_server
				fi
				# If it was 'half', it is completly set now
				test "$target_rl_was_set" = half &&
					target_rl_was_set=yes
			fi
			debug "Progress(): Target Runlevel3: $target_rl"
			debug "Progress(): Target RL set3: $target_rl_was_set"
		fi
		# Unset $prctg so that if unknown, it is not set. It cannot be
		# unset earlier since start_server() might need it
		unset prctg
		debug "Progress(): script=$script, scripts=$scripts"
		# Note that $script might be something like
		# "/bin/sh /etc/rcS.d/S01splashy"
		for file in $scripts; do
			var=$(($var + 1))
			test $file = "$script" && xscript=$var && break
		done
		if test -z "$xscript"; then
			# If the script wasn't found, ignore it. Maybe a
			# K script in runlevel S
			debug "Progress: NOT FOUND!!: $script,$scripts"
			return 1
		elif test 0"$xscript" -gt 0"$nscripts"; then
			# If we are here it is because we passed the
			# last step, so complete at once
			debug "Progress: Overflowed"
			return 0
		fi
		# Don't calculate progress if it is not going to be used
		if is_enabled "$SET_PROGRESS"; then
			# If no scripts were detected, return and keep looping
			# Do not return true (break main loop) since this could
			# be runlevel S, so there could be scripts in next
			# runlevel
			# The script is already prepared to detect when no
			# scripts are left to run, even when $nscript == 0
			# Anyway, this should never happen. If it did, it is
			# most probably a bug in the get_scripts() function
			if [ $nscripts = 0 ]; then
				debug "Progress: No scripts detected!"
				return 1
			fi
			# Notice that the calculations are all in a single
			# command and with a single division so that there is
			# little loss in integer division
			# If the next runlevel cannot be handled now (when in
			# first runlevel S), suppose S will take 75% of the time
			if test -z "$target_rl_was_set" ||
			   test -n "$target_rl" -a "$target_rl" != "$level"
			   then
				prctg="$(($xscript * 100 * 3 /(4 * $nscripts)))"
				debug "Progress: PrctgAlgo: S"
			# Previously the booting runlevel wasn't know but it is
			# now known, so resume from the suppose 75% progress
			elif test "$target_rl" = "$level"; then
				prctg="$(($xscript * 100 /(4 * $nscripts) +75))"
				debug "Progress: PrctgAlgo: !S"
			# Default behaviour
			else
				prctg="$(($xscript * 100 / $nscripts))"
				debug "Progress: PrctgAlgo: Other"
			fi
#			# When going down, if set in configuration, invert
#			# percentage
#			if test $RUNLEVEL = 0 -o $RUNLEVEL = 6; then
#				#debug "Progress: Now: 0 or 6"
#				if is_enabled "$INVERT_DOWN"; then
#					prctg="$((100 - $prctg))"
#					debug "Progress: Down: Inverse"
#				fi
#			fi
		fi
		test "$prevcyclerl" != "$level" && prevcyclerl="$level"
		return 1
	}

	is_enabled "$EXTERN_PROGRESS" ||
	# This function performs signals handling
	signal_handler() {
		debug "Signal Handler: $1"
		case $1 in
		ABRT)
			clean_exit 0
			;;
		TERM)
			send_command exit
			clean_exit 0
			;;
		USR1)
			perform_complete
			;;
		USR2)
			config_parse
			start_server
			;;
		*)
			error "Signal Handler: Invalid signal $1"
			;;
		esac
	}
	### END FUNCTIONS EXCLUSIVE FOR THIS MODE

	# If the server was already set to run (in start mode), don't attempt to
	# start it now. Otherwise, if the user had been fast enough to enter
	# verbose mode, it would splash again
	if test -z "$SERVER_RUNNING"; then
		# See if proc fs is mounted. If it isn't sleep until it is
		# Since it should be mounted pretty soon, we can safely set it
		# to wake up in very low frequencies
		# Even if we already checked if it exists, it does noharm to
		# check again
		while test ! -e /proc/1; do sleep -- $SC_SLEEP; done
		# Check also that the framebuffer device exists or wait for it
		# to be created, since some systems may rely on udev creating
		# it. But before, make sure that udev is going to run, otherwise
		# just go on and report the failure when splashy server fails
		if test ! -e "$CONSOLE" -o ! -e "$FRAMEBUFFER"; then
			# Only wait if udev is launched. Otherwise just fail
			if file_exists "/etc/rcS.d/*udev"; then
				i=0
				debug "Waiting framebuffer or console devices"
				# Cycle 90 times maximum
				while test "$i" -lt 90; do
					sleep -- $SC_SLEEP
					if test -e "$FRAMEBUFFER" -a \
					  -e "$CONSOLE"; then
						fbdev_found=1
						break
					fi
					i=$(($i + 1))
				done
			fi
			test 0$fbdev_found -eq 0 && clean_exit 0
		fi
		# Run splashy-server daemon
		is_server_running
		server_status=$?
		if test $server_status -eq 0 && is_number "$pid" 0; then
			if test $RUNLEVEL = 0 -o $RUNLEVEL = 6; then
				debug "Server: Running (will restart it)"
				# If it is already running but level is 0 or 6,
				# or there is more than one instance running,
				# kill it (them) and run it again. In the case
				#of going down it would get killed by runlevel
				# shutdown scripts otherwise
				silent -S kill $pid
				start_server
			else
				# If it is already running, we are not going
				# down and there is only one instance running,
				# use it
				# Notice it MUST be restarted when going down
				# Read few lines below the reason
				debug "Server: Running already"
				true
			fi
		else
			debug "Server: Not running"
			start_server
		fi
	else debug "Client: Server was started already"
	fi
	# In extern_progress mode Splashy server must be running now, so exit
	is_enabled "$EXTERN_PROGRESS" && clean_exit 0
	# Set $prctg and $oldprctg to the initial progress value
	is_enabled "$SET_PROGRESS" && test -z "$prctg" &&
#	  if test "$tlevel" = 0 -o "$tlevel" = 6 &&
#	     is_enabled "$INVERT_DOWN"; then
#		prctg=100
#		# If in inverse mode, set bar to 100 from the very start since
#		# Splashy initializes it to 0%
#		send_command "progress $prctg"
#	  else
		prctg=0
#	  fi
	# Do not assume $oldprctg will be set later: if the process is complete
	# before it hits progress2percent(), it will never be set
	oldprctg="$prctg"

	# Trap signals to control remotely a running instance of this script
	# There is no need to trap them before this point
	# SIGINT and SIGQUIT cannot be handled (at least by bash) here
        #LM: man bash and look for SIGNALS
	trap "signal_handler ABRT" ABRT # ctrl+c
	trap "signal_handler TERM" TERM # kill
	trap "signal_handler USR1" USR1
	trap "signal_handler USR2" USR2

	# Now go drawing the percentage in frequencies of $SLEEP_TIME seconds
	# By the time we get here, we can expect some progress has been done
	# (otherwise /proc would not be mounted), so don't sleep initially
	debug "Progress: Start (sleep: $SLEEP_TIME)"
	until get_status || progress2percent; do
		# Write down the PID. This must be done every time it is
		# possible, since it may be deleted during the client's
		# execution (see the boot notes in the comment at the beginning
		# of the script
		# When runlevel S finishes, all mounts should be correctly done,
		# so we can assume that the pidfile is not correctly set
		set_pid_file
		if test -n "$prctg" -a "$oldprctg" != "$prctg"; then
#			# Send Splashy the percentage to increment the bar
#			i=$(($prctg - $oldprctg));debug S: $prctg-$oldprctg=$i
#			test $i -lt 0 && debug "GOSH!!!!"
#			debug && inc_count="$((${inc_count:-0} + $i))"
#			debug "Current count goes up to $inc_count"
#			send_command "progress $(($prctg - $oldprctg))"
			# Send Splashy the percentage to display
			debug "S: $prctg"
			send_command "progress $prctg"
		else debug "Progress: no progress update reported"
		fi
		debug "Progress: Next iteration"
		sleep -- $SLEEP_TIME
	done
	trap - INT USR1 USR2
	perform_complete
	;;

[Ss]top)
	# In extern_progress mode, just stop server
	if ! is_enabled "$EXTERN_PROGRESS" && is_client_running; then
		debug "Stop: Client $pid"
		silent -S kill -USR1 $pid
	else
		# Stop server. Only when it was impossible to stop signal the
		# client
		if is_server_running; then
			debug "Stop: Server"
			# Notice that if the config file path changed, this
			# may fail
			send_command "exit"
		else
			false
		fi
	fi
	test $? -ne 0 -a $mode = stop &&
		error "Unable to stop Splashy. Most probably it isn't running" 0
	;;

restart)
	# If in extern_progress mode, just restart server
	if is_enabled "$EXTERN_PROGRESS"; then
		splashy_update exit
		sleep 1
		if is_server_running; then
			for proc in $pid; do
				kill -9 $proc
			done
		fi
		start_server
	elif is_client_running; then
		debug "Restart: Client: Running"
		"$0" stop && "$0" start
	else
		debug "Restart: Server: Not running"
		"$0" start
	fi
	;;

try-restart | force-reload)
	# If in extern_progress mode, just restart server
	if is_enabled "$EXTERN_PROGRESS"; then
		"$0" restart
	elif is_client_running; then
		debug "Try-Restart: Yes"
		"$0" stop && "$0" start
	else
		debug "Try-Restart: No"
		exit 1
	fi
	;;

reload)
	# If in extern_progress mode, just restart server
	if is_enabled "$EXTERN_PROGRESS"; then
		"$0" restart
		exit 0
	fi
	# Read config again and restart server at the same point it was (since
	# this is the only way to make the server read the theme again)
	is_client_running && silent -S kill -USR2 $pid
	;;

status)
	
	# If in extern_progress mode, just show server status
	if is_enabled "$EXTERN_PROGRESS"; then
		if is_server_running; then
			echo Running
			exit 0
		elif test -e "$PIDFILE"; then
			echo "Not running, but pid file exists"
			exit 1
		else
			echo "Not running"
			exit 3
		fi
	# Report client status only. Client should die once server dies
	elif is_client_running; then
		echo Running
		exit 0
	elif test -e "$CLIENT_PIDFILE"; then
		# Do not use error() function: this is not an error
		echo "Not running, but pid file exists"
		exit 1
	else
		# Do not use error() function: this is not an error
		echo "Not running"
		exit 3
	fi
	;;

*)
	# This is not an init error message, so do not use error() funciton
	echo "Usage: start|stop|status|restart|try-restart|reload|force-reload"
	test "$mode" = help && exit 0
	exit 1
	;;
esac
### END ACTIONS
