# Distributed under the terms of the GNU General Public License v2 # Author: Michal Januszewski <spock@gentoo.org> # Maintainer: Michal Januszewski <spock@gentoo.org> # This file is a part of splashutils. The functions contained in this # file are meant to be used by hook scripts in splash themes or by # initscript systems. The code will be kept distro-agnostic to facilitate # portability. It should also contain POSIX compatible code. No bashisms # allowed! # #################################################################### # Change any settings ONLY if you are sure what you're doing. # Don't cry if it breaks afterwards. # #################################################################### # The splash scripts need a cache which can be guaranteed to be # both readable and writable at all times, even when the root fs # is mounted read-only. To that end, an in-RAM fs is used. Valid # values for spl_cachetype are 'tmpfs' and 'ramfs'. spl_cachesize # is a size limit in KB, and it should probably be left with the # default value. export spl_cachesize="4096" export spl_cachetype="tmpfs" export spl_cachedir="//lib/splash/cache" export spl_tmpdir="//lib/splash/tmp" export spl_fifo="${spl_cachedir}/.splash" export spl_pidfile="${spl_cachedir}/daemon.pid" export spl_util="//bin/splash_util.static" export spl_daemon="//sbin/fbsplashd.static" export spl_decor="//sbin/fbcondecor_ctl.static" export spl_bindir="//lib/splash/bin" # This is the main function which handles all events. # Accepted parameters: # svc_start <name> # svc_stop <name> # svc_started <name> # svc_stopped <name> # svc_start_failed <name> # svc_stop_failed <name> # svc_input_begin <name> # svc_input_end <name> # rc_init <internal_runlevel> - used to distinguish between 'boot' and 'sysinit' # rc_exit # critical splash() { local event="$1" shift # Reload the splash settings in rc_init. We could have set them wrong the # first time splash_setup was called (when splash-functions.sh was first # sourced) if /proc wasn't mounted. if [ "${event}" = "rc_init" ]; then splash_setup "force" else splash_setup fi [ "${SPLASH_MODE_REQ}" = "off" ] && return # Prepare the cache here -- rc_init-pre might want to use it if [ "${event}" = "rc_init" ]; then if [ "${RUNLEVEL}" = "S" -a "$1" = "sysinit" ]; then splash_cache_prep 'start' || return elif [ "${RUNLEVEL}" = "6" -o "${RUNLEVEL}" = "0" ]; then # Check if the splash cachedir is mounted readonly. If it is, # we need to mount a tmpfs over it. if ! touch "${spl_cachedir}/message" 2>/dev/null ; then splash_cache_prep 'stop' || return fi fi fi local args="" if [ "${event}" = "rc_init" -o "${event}" = "rc_exit" ]; then args="$* ${RUNLEVEL}" elif [ "${event}" = "svc_started" -o "${event}" = "svc_stopped" ]; then if [ -z "$2" ]; then # Backwards compatibility hack. Add a 0 to the arguments to simulate # an error code. args="$* 0" else args="$*" # Backwards compatibility: translate an error condition (non-zero # error code) into an appropriate event. if [ "$2" != "0" ]; then if [ "${event}" = "svc_started" ]; then event="svc_start_failed" else event="svc_stop_failed" fi fi fi else args="$*" fi splash_profile "pre ${event} ${args}" # Handle -pre event hooks if [ -x "/etc/splash/${SPLASH_THEME}/scripts/${event}-pre" ]; then /etc/splash/"${SPLASH_THEME}"/scripts/${event}-pre ${args} fi case "$event" in svc_start) splash_svc_start "$1";; svc_stop) splash_svc_stop "$1";; svc_started) splash_svc "$1" "start";; svc_stopped) splash_svc "$1" "stop";; svc_start_failed) splash_svc_fail "$1" "start";; svc_stop_failed) splash_svc_fail "$1" "stop";; svc_input_begin) splash_input_begin "$1";; svc_input_end) splash_input_end "$1";; rc_init) splash_init "$1" "${RUNLEVEL}";; rc_exit) splash_exit "${RUNLEVEL}";; critical) splash_verbose;; esac splash_profile "post ${event} ${args}" # Handle -post event hooks if [ -x "/etc/splash/${SPLASH_THEME}/scripts/${event}-post" ]; then /etc/splash/"${SPLASH_THEME}"/scripts/${event}-post ${args} fi return 0 } splash_setup() { # If it's already set up, let's not waste time on parsing the config # files again if [ "${SPLASH_THEME}" != "" -a "${SPLASH_TTY}" != "" -a "$1" != "force" ]; then return 0 fi export SPLASH_EFFECTS="" export SPLASH_SANITY="" export SPLASH_TEXTBOX="no" export SPLASH_MODE_REQ="off" export SPLASH_PROFILE="off" export SPLASH_THEME="default" export SPLASH_TTY="16" export SPLASH_KDMODE="TEXT" export SPLASH_AUTOVERBOSE="0" export SPLASH_BOOT_MESSAGE="Booting the system (\$progress%)... Press F2 for verbose mode." export SPLASH_SHUTDOWN_MESSAGE="Shutting down the system (\$progress%)... Press F2 for verbose mode." export SPLASH_REBOOT_MESSAGE="Rebooting the system (\$progress%)... Press F2 for verbose mode." export SPLASH_XSERVICE="xdm" [ -f /etc/splash/splash ] && . /etc/splash/splash [ -f /etc/conf.d/splash ] && . /etc/conf.d/splash [ -f /etc/conf.d/fbcondecor ] && . /etc/conf.d/fbcondecor if [ -f /proc/cmdline ]; then options=$(grep -o 'splash=[^ ]*' /proc/cmdline) # Execute this loop over $options so that we can process multiple # splash= arguments on the kernel command line. Useful for adjusting # splash parameters from ISOLINUX. for opt in ${options} ; do options=${opt#*=} for i in $(echo "${options}" | sed -e 's/,/ /g') ; do case ${i%:*} in theme) SPLASH_THEME=${i#*:} ;; tty) SPLASH_TTY=${i#*:} ;; verbose) SPLASH_MODE_REQ="verbose" ;; silent) SPLASH_MODE_REQ="silent" ;; kdgraphics) SPLASH_KDMODE="GRAPHICS" ;; profile) SPLASH_PROFILE="on" ;; insane) SPLASH_SANITY="insane" ;; esac done done fi } splash_get_boot_message() { if [ "${RUNLEVEL}" = "6" ]; then echo "${SPLASH_REBOOT_MESSAGE}" elif [ "${RUNLEVEL}" = "0" ]; then echo "${SPLASH_SHUTDOWN_MESSAGE}" else echo "${SPLASH_BOOT_MESSAGE}" fi } splash_start() { if [ "${SPLASH_MODE_REQ}" = "verbose" ]; then ${spl_decor} -c on 2>/dev/null return 0 elif [ "${SPLASH_MODE_REQ}" != "silent" ]; then return 0 fi # Display a warning if the system is not configured to display init messages # on tty1. This can cause a lot of problems if it's not handled correctly, so # we don't allow silent splash to run on incorrectly configured systems. if [ "${SPLASH_MODE_REQ}" = "silent" -a "${SPLASH_SANITY}" != "insane" ]; then if [ -z "$(grep -E '(^| )CONSOLE=/dev/tty1( |$)' /proc/cmdline)" -a \ -z "$(grep -E '(^| )console=tty1( |$)' /proc/cmdline)" ]; then clear splash_warn "You don't appear to have a correct console= setting on your kernel" splash_warn "command line. Silent splash will not be enabled. Please add" splash_warn "console=tty1 or CONSOLE=/dev/tty1 to your kernel command line" splash_warn "to avoid this message." if [ -n "$(grep 'CONSOLE=/dev/tty1' /proc/cmdline)" -o \ -n "$(grep 'console=tty1' /proc/cmdline)" ]; then splash_warn "Note that CONSOLE=/dev/tty1 and console=tty1 are general parameters and" splash_warn "not splash= settings." fi return 1 fi if [ -n "$(grep -E '(^| )CONSOLE=/dev/tty1( |$)' /proc/cmdline)" ]; then mount -n --bind / ${spl_tmpdir} if [ ! -c "${spl_tmpdir}/dev/tty1" ]; then umount -n ${spl_tmpdir} splash_warn "The filesystem mounted on / doesn't contain the /dev/tty1 device" splash_warn "which is required for the silent splash to function properly." splash_warn "Silent splash will not be enabled. Please create the appropriate" splash_warn "device node to avoid this message." return 1 fi umount -n ${spl_tmpdir} fi fi rm -f "${spl_pidfile}" # Prepare the communications FIFO rm -f "${spl_fifo}" 2>/dev/null mkfifo "${spl_fifo}" local options="" [ "${SPLASH_KDMODE}" = "GRAPHICS" ] && options="--kdgraphics" [ -n "${SPLASH_EFFECTS}" ] && options="${options} --effects=${SPLASH_EFFECTS}" [ "${SPLASH_TEXTBOX}" = "yes" ] && options="${options} --textbox" local ttype="bootup" if [ "${RUNLEVEL}" = "6" ]; then ttype="reboot" elif [ "${RUNLEVEL}" = "0" ]; then ttype="shutdown" fi # Start the splash daemon BOOT_MSG="$(splash_get_boot_message)" ${spl_daemon} --theme="${SPLASH_THEME}" --pidfile="${spl_pidfile}" --type=${ttype} ${options} # Set the silent TTY and boot message splash_comm_send "set tty silent ${SPLASH_TTY}" if [ "${SPLASH_MODE_REQ}" = "silent" ]; then splash_comm_send "set mode silent" splash_comm_send "repaint" ${spl_decor} -c on 2>/dev/null fi splash_comm_send "set autoverbose ${SPLASH_AUTOVERBOSE}" splash_set_event_dev return 0 } ########################################################################### # Cache-related functions ########################################################################### splash_cache_prep() { # Mount an in-RAM filesystem at spl_cachedir mount -ns -t "${spl_cachetype}" cachedir "${spl_cachedir}" \ -o rw,mode=0644,size="${spl_cachesize}"k retval="$?" if [ ${retval} -ne 0 ]; then splash_err "Unable to create splash cache - switching to verbose." splash_verbose return "${retval}" fi } # args: list of files to save when the cache is umounted # Note that the 'profile' file is already handled and thus shouldn't # be included in the list. splash_cache_cleanup() { # Don't try to clean anything up if the cachedir is not mounted. [ -z "$(grep ${spl_cachedir} /proc/mounts)" ] && return; # Create the temp dir if necessary. if [ ! -d "${spl_tmpdir}" ]; then mkdir -p "${spl_tmpdir}" 2>/dev/null [ "$?" != "0" ] && return fi # Make sure the splash daemon is dead. if [ -n "$(pgrep fbsplashd)" ]; then sleep 1 killall -9 "${spl_daemon##*/}" 2>/dev/null fi # If /etc is not writable, don't update /etc/mtab. If it is # writable, update it to avoid stale mtab entries (bug #121827). local mntopt="" [ -w /etc/mtab ] || mntopt="-n" mount ${mntopt} --move "${spl_cachedir}" "${spl_tmpdir}" 2>/dev/null # Don't try to copy anything if the cachedir is not writable. [ -w "${spl_cachedir}" ] || return if [ "${SPLASH_PROFILE}" != "off" ]; then cp -a "${spl_tmpdir}/profile" "${spl_cachedir}" 2>/dev/null fi while [ -n "$1" ]; do cp -a "${spl_tmpdir}/$1" "${spl_cachedir}" 2>/dev/null shift done umount -l "${spl_tmpdir}" 2>/dev/null } ########################################################################### # Common functions ########################################################################### # Sends data to the splash FIFO after making sure there's someone # alive on the other end to receive it. splash_comm_send() { if [ -z "`pidof $(basename ${spl_daemon})`" ]; then return 1 else splash_profile "comm $*" echo "$*" > "${spl_fifo}" & fi } # Returns the current splash mode. splash_get_mode() { local ctty="${spl_bindir}/fgconsole" local mode="$(${spl_util})" if [ "${mode}" = "silent" ]; then echo "silent" else if [ -z "$(${spl_decor} -c getstate --tty=${ctty} 2>/dev/null | grep off)" ]; then echo "verbose" else echo "off" fi fi } # chvt <n> # -------- # Switches to the n-th tty. chvt() { local ntty=$1 # if [ -x /usr/bin/chvt ] ; then # /usr/bin/chvt ${ntty} # else printf "\e[12;${ntty}]" # fi } # Switches to verbose mode. splash_verbose() { # chvt 1 /bin/true } # Switches to silent mode. splash_silent() { splash_comm_send "set mode silent" } # Saves profiling information splash_profile() { if [ "${SPLASH_PROFILE}" = "on" ]; then echo "$(cat /proc/uptime | cut -f1 -d' '): $*" >> "${spl_cachedir}/profile" fi } # Set the input device if it exists. This will make it possible to use F2 to # switch from verbose to silent. splash_set_event_dev() { local t="$(grep -Hsi keyboard /sys/class/input/input*/name | sed -e 's#.*input\([0-9]*\)/name.*#event\1#')" if [ -z "${t}" ]; then t="$(grep -Hsi keyboard /sys/class/input/event*/device/driver/description | grep -o 'event[0-9]\+')" if [ -z "${t}" ]; then for i in /sys/class/input/input* ; do if [ "$((0x$(cat $i/capabilities/ev) & 0x100002))" = "1048578" ]; then t="$(echo $i | sed -e 's#.*input\([0-9]*\)#event\1#')" fi done if [ -z "${t}" ]; then # Try an alternative method of finding the event device. The idea comes # from Bombadil <bombadil(at)h3c.de>. We're couting on the keyboard controller # being the first device handled by kbd listed in input/devices. t="$(/bin/grep -s -m 1 '^H: Handlers=kbd' /proc/bus/input/devices | grep -o 'event[0-9]*')" fi fi fi [ -n "${t}" ] && splash_comm_send "set event dev /dev/input/${t}" } ########################################################################### # Service ########################################################################### # args: <svc> <action> splash_svc() { local srv="$1" local act="$2" if [ "${act}" = "start" ]; then splash_svc_update "${srv}" "svc_started" if [ "${srv}" = "gpm" ]; then splash_comm_send "set gpm" splash_comm_send "repaint" fi splash_comm_send "log Service '${srv}' started." else splash_svc_update "${srv}" "svc_stopped" splash_comm_send "log Service '${srv}' stopped." fi splash_update_progress "${srv}" } # args: <svc> <action> splash_svc_fail() { local srv="$1" local act="$2" if [ "${SPLASH_VERBOSE_ON_ERRORS}" = "yes" ]; then splash_verbose return 1 fi if [ "${act}" = "start" ]; then splash_svc_update "${srv}" "svc_start_failed" splash_comm_send "log Service '${srv}' failed to start." else splash_svc_update "${srv}" "svc_stop_failed" splash_comm_send "log Service '${srv}' failed to stop." fi splash_update_progress "${srv}" } # args: <svc> <state> # # Inform the splash daemon about service status changes. splash_svc_update() { splash_comm_send "update_svc $1 $2" } # args: <svc> splash_svc_start() { local svc="$1" splash_svc_update "${svc}" "svc_start" splash_comm_send "paint" } # args: <svc> splash_svc_stop() { local svc="$1" splash_svc_update "${svc}" "svc_stop" splash_comm_send "paint" } # args: <svc> splash_input_begin() { local svc="$1" if [ "$(splash_get_mode)" = "silent" ]; then splash_verbose export SPL_SVC_INPUT_SILENT="${svc}" fi } # args: <svc> splash_input_end() { local svc="$1" if [ "${SPL_SVC_INPUT_SILENT}" = "${svc}" ]; then splash_silent unset SPL_SVC_INPUT_SILENT fi } ########################################################################### # Framebuffer Console Decorations functions ########################################################################### fbcondecor_supported() { [ -e /dev/fbsplash -o -e /dev/fbcondecor ] } # args: <theme> <tty> fbcondecor_set_theme() { local theme=$1 local tty=$2 [ -x ${spl_decor} ] || return 1 ${spl_decor} --tty="${tty}" -t "${theme}" -c setcfg || return 1 ${spl_decor} --tty="${tty}" -t "${theme}" -c setpic -q ${spl_decor} --tty="${tty}" -c on } ########################################################################### # Service list ########################################################################### # splash_svclist_get <type> # ------------------------- # type: # - start - to get a list of services to be started during bootup # - stop - to get a list of services to be stopped during shutdown/reboot splash_svclist_get() { if [ "$1" = "start" -a -r "${spl_cachedir}/svcs_start" ]; then cat "${spl_cachedir}/svcs_start" elif [ "$1" = "stop" -a -r "${spl_cachedir}/svcs_stop" ]; then cat "${spl_cachedir}/svcs_stop" fi } splash_warn() { echo "$*" } splash_err() { echo "$*" } ############################################################################ # Functions to be overridden by distro-specific code ########################################################################### # args: <internal_runlevel> <runlevel> # # This function is called when an 'rc_init' event takes place, # i.e. when the runlevel is changed. # It is normally used to initialize any internal splash-related # variables (e.g. ones used to track the boot progress), calculate # and save the service list and call `splash_start` in appropriate # runlevels. # # Note that the splash cache is already intialized when this # function is called. splash_init() { if [ "$2" = "6" -o "$2" = "0" -o "$2" = "S" -a "$1" = "sysinit" ]; then splash_start fi } # args: <runlevel> # # This function is called when an 'rc_exit' event takes place, # i.e. at the end of processes all initscript from a runlevel. splash_exit() { # If we're in sysinit or rebooting, do nothing. [ "$1" = "S" -o "$1" = "6" -o "$1" = "0" ] && return 0 splash_comm_send "exit" splash_cache_cleanup } # args: <svc> # # This function is called whenever the progress variable should be # updated. It should recalculate the progress and send it to the # splash daemon. splash_update_progress() { # splash_comm_send "progress ${progress}" # splash_comm_send "paint" return } # Export functions if we're running bash. if [ -n "${BASH}" ]; then export -f splash export -f splash_setup export -f splash_get_boot_message export -f splash_start export -f splash_cache_prep export -f splash_cache_cleanup export -f splash_comm_send export -f splash_get_mode export -f chvt export -f splash_verbose export -f splash_silent export -f splash_profile export -f splash_set_event_dev export -f splash_svclist_get fi # Load any supplementary splash functions. for i in /sbin/splash-functions-*.sh ; do [ -r "${i}" ] && . "${i}" done splash_setup # vim:ts=4