From 5af58b45146ab5253ca964738f4e45287bf963d4 Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Thu, 5 Apr 2007 11:18:42 +0000 Subject: Rewrite the core parts in C. We now provide librc so other programs can query runlevels, services and state without using bash. We also provide libeinfo so other programs can easily use our informational functions. As such, we have dropped the requirement of using bash as the init script shell. We now use /bin/sh and have strived to make the scripts as portable as possible. Shells that work are bash and dash. busybox works provided you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you should disable find too. zsh and ksh do not work at this time. Networking support is currently being re-vamped also as it was heavily bash array based. As such, a new config format is available like so config_eth0="1.2.3.4/24 5.6.7.8/16" or like so config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'" We will still support the old bash array format provided that /bin/sh IS a link it bash. ChangeLog for baselayout-1 can be found in our SVN repo. --- sh/Makefile | 7 + sh/functions.sh | 157 ++++++++++++++ sh/gendepends.sh | 58 +++++ sh/init-common-post.sh | 22 ++ sh/init-functions.sh | 62 ++++++ sh/net.sh | 566 +++++++++++++++++++++++++++++++++++++++++++++++++ sh/rc-functions.sh | 60 ++++++ sh/rc-help.sh | 262 +++++++++++++++++++++++ sh/rc-mount.sh | 70 ++++++ sh/runscript.sh | 74 +++++++ 10 files changed, 1338 insertions(+) create mode 100644 sh/Makefile create mode 100644 sh/functions.sh create mode 100755 sh/gendepends.sh create mode 100644 sh/init-common-post.sh create mode 100644 sh/init-functions.sh create mode 100755 sh/net.sh create mode 100755 sh/rc-functions.sh create mode 100755 sh/rc-help.sh create mode 100644 sh/rc-mount.sh create mode 100755 sh/runscript.sh (limited to 'sh') diff --git a/sh/Makefile b/sh/Makefile new file mode 100644 index 00000000..ac50fa90 --- /dev/null +++ b/sh/Makefile @@ -0,0 +1,7 @@ +DIR = /$(LIB)/rcscripts/sh +FILES = functions.sh init-functions.sh init-common-post.sh \ + rc-functions.sh rc-mount.sh +EXES = gendepends.sh net.sh rc-mount.sh rc-help.sh runscript.sh + +TOPDIR = .. +include $(TOPDIR)/default.mk diff --git a/sh/functions.sh b/sh/functions.sh new file mode 100644 index 00000000..d1327ad5 --- /dev/null +++ b/sh/functions.sh @@ -0,0 +1,157 @@ +# Copyright 1999-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +# Please keep this useable by every shell in portage + +RC_GOT_FUNCTIONS="yes" + +eindent() { + if [ -n "${RC_EBUFFER}" ] ; then + "${RC_LIBDIR}"/bin/eindent + else + RC_EINDENT=$((${RC_EINDENT:-0} + 2)) + [ "${RC_EINDENT}" -gt 40 ] && RC_EINDENT=40 + export RC_EINDENT + fi +} + +eoutdent() { + if [ -n "${RC_EBUFFER}" ] ; then + "${RC_LIBDIR}"/bin/eoutdent + else + RC_EINDENT=$((${RC_EINDENT:-0} - 2)) + [ "${RC_EINDENT}" -lt 0 ] && RC_EINDENT=0 + fi + return 0 +} + +# void esyslog(char* priority, char* tag, char* message) +# +# use the system logger to log a message +# +esyslog() { + local pri= tag= + + if [ -x /usr/bin/logger ] ; then + pri="$1" + tag="$2" + + shift 2 + [ -z "$*" ] && return 0 + + /usr/bin/logger -p "${pri}" -t "${tag}" -- "$*" + fi + + return 0 +} + +# Safer way to list the contents of a directory, +# as it do not have the "empty dir bug". +# +# char *dolisting(param) +# +# print a list of the directory contents +# +# NOTE: quote the params if they contain globs. +# also, error checking is not that extensive ... +# +dolisting() { + local x= y= mylist= mypath="$*" + + # Here we use file globbing instead of ls to save on forking + for x in ${mypath} ; do + [ ! -e "${x}" ] && continue + + if [ -L "${x}" -o -f "${x}" ] ; then + mylist="${mylist} "${x} + elif [ -d "${x}" ] ; then + [ "${x%/}" != "${x}" ] && x=${x%/} + + for y in "${x}"/* ; do + [ -e "${y}" ] && mylist="${mylist} ${y}" + done + fi + done + + echo "${mylist# *}" +} + +# bool is_older_than(reference, files/dirs to check) +# +# return 0 if any of the files/dirs are newer than +# the reference file +# +# EXAMPLE: if is_older_than a.out *.o ; then ... +is_older_than() { + local x= ref="$1" + shift + + for x in "$@" ; do + [ -e "${x}" ] || continue + # We need to check the mtime if it's a directory too as the + # contents may have changed. + [ "${x}" -nt "${ref}" ] && return 0 + [ -d "${x}" ] && is_older_than "${ref}" "${x}"/* && return 0 + done + + return 1 +} + +uniqify() { + local result= + while [ -n "$1" ] ; do + case " ${result} " in + *" $1 "*) ;; + *) result="${result} $1" ;; + esac + shift + done + echo "${result# *}" +} + +KV_to_int() { + [ -z $1 ] && return 1 + + local KV_MAJOR=${1%%.*} + local x=${1#*.} + local KV_MINOR=${x%%.*} + x=${1#*.*.} + local KV_MICRO=${x%%-*} + local KV_int=$((${KV_MAJOR} * 65536 + ${KV_MINOR} * 256 + ${KV_MICRO} )) + + # We make version 2.2.0 the minimum version we will handle as + # a sanity check ... if its less, we fail ... + [ "${KV_int}" -lt 131584 ] && return 1 + + echo "${KV_int}" +} + +# Setup a basic $PATH. Just add system default to existing. +# This should solve both /sbin and /usr/sbin not present when +# doing 'su -c foo', or for something like: PATH= rcscript start +case "${PATH}" in + /lib/rcscripts/bin:/bin:/sbin:/usr/bin:/usr/sbin) ;; + /lib/rcscripts/bin:/bin:/sbin:/usr/bin:/usr/sbin:*) ;; + *) export PATH="/lib/rcscripts/bin:/bin:/sbin:/usr/bin:/usr/sbin:${PATH}" ;; +esac + +for arg in "$@" ; do + case "${arg}" in + --nocolor|--nocolour) + export RC_NOCOLOR="yes" + ;; + esac +done + +if [ "${RC_NOCOLOR}" != "yes" -a -z "${GOOD}" ] ; then + if color_terminal ; then + GOOD=$'\e[32;01m' + WARN=$'\e[33;01m' + BAD=$'\e[31;01m' + HILITE=$'\e[36;01m' + BRACKET=$'\e[34;01m' + NORMAL=$'\e[0m' + fi +fi + +# vim: set ts=4 : diff --git a/sh/gendepends.sh b/sh/gendepends.sh new file mode 100755 index 00000000..aa494f03 --- /dev/null +++ b/sh/gendepends.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# Shell wrapper to list our dependencies +# Copyright 2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +. /etc/init.d/functions.sh + +need() { + [ -n "$*" ] && echo "${SVCNAME} ineed $*" +} +use() { + [ -n "$*" ] && echo "${SVCNAME} iuse $*" +} +before() { + [ -n "$*" ] && echo "${SVCNAME} ibefore $*" +} +after() { + [ -n "$*" ] && echo "${SVCNAME} iafter $*" +} +provide() { + [ -n "$*" ] && echo "${SVCNAME} iprovide $*" +} +depend() { + : +} + +cd /etc/init.d +for SVCNAME in * ; do + [ -x "${SVCNAME}" ] || continue + case "${SVCNAME}" in + *.sh) continue ;; + esac + + SVCNAME=${SVCNAME##*/} + ( + if . /etc/init.d/"${SVCNAME}" ; then + rc_c=${SVCNAME%%.*} + if [ -n "${rc_c}" -a "${rc_c}" != "${SVCNAME}" ] ; then + [ -e /etc/conf.d/"${rc_c}" ] && . /etc/conf.d/"${rc_c}" + fi + unset rc_c + + [ -e /etc/conf.d/"${SVCNAME}" ] && . /etc/conf.d/"${SVCNAME}" + + echo "${SVCNAME}" + depend + + # Add any user defined depends + need ${RC_NEED} + use ${RC_USE} + before ${RC_BEFORE} + after ${RC_AFTER} + provide ${RC_PROVIDE} + fi + ) +done + +# vim: set ts=4 : diff --git a/sh/init-common-post.sh b/sh/init-common-post.sh new file mode 100644 index 00000000..ec535eb6 --- /dev/null +++ b/sh/init-common-post.sh @@ -0,0 +1,22 @@ +# Copyright 1999-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +# mount $svcdir as something we can write to if it's not rw +# On vservers, / is always rw at this point, so we need to clean out +# the old service state data +if touch "${RC_SVCDIR}/.test" 2>/dev/null ; then + rm -rf "${RC_SVCDIR}/.test" \ + $(ls -d1 "${RC_SVCDIR:-/lib/rcscripts/init.d}"/* 2>/dev/null | \ + grep -Ev "/(deptree|ksoftlevel)$") +else + mount_svcdir +fi + +echo "sysinit" > "${RC_SVCDIR}/softlevel" + +# sysinit is now done, so allow init scripts to run normally +[ -e /dev/.rcsysinit ] && rm -f /dev/.rcsysinit + +exit 0 + +# vim: set ts=4 : diff --git a/sh/init-functions.sh b/sh/init-functions.sh new file mode 100644 index 00000000..59f0eb6b --- /dev/null +++ b/sh/init-functions.sh @@ -0,0 +1,62 @@ +# Copyright 1999-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +# void try(command) +# +# Try to execute 'command', if it fails, drop to a shell. +# +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 + #splash "critical" & + eend 1 + eerror "The \"$*\" command failed with error:" + eerror " ${errstr#*: }" + echo + eerror "Since this is a critical task, startup cannot continue." + echo + single_user + fi + + return ${retval} +} + +# bool check_statedir(dir) +# +# Check that 'dir' exists, if not, drop to a shell. +# +check_statedir() { + [ -z "$1" ] && return 0 + + if [ ! -d "$1" ] ; then + if ! mkdir -p "$1" &>/dev/null ; then + #splash "critical" & + echo + eerror "For Gentoo to function properly, \"$1\" needs to exist." + if [ "${RC_FORCE_AUTO}" = "yes" ] ; then + eerror "Attempting to create \"$1\" for you ..." + mount -o remount,rw / + mkdir -p "$1" + fi + if [ ! -d "$1" ] ; then + eerror "Please mount your root partition read/write, and execute:" + echo + eerror " # mkdir -p $1" + echo; echo + single_user + fi + fi + fi + + return 0 +} + +# vim: set ts=4 : diff --git a/sh/net.sh b/sh/net.sh new file mode 100755 index 00000000..c839dd4c --- /dev/null +++ b/sh/net.sh @@ -0,0 +1,566 @@ +#!/sbin/runscript +# Copyright 1999-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +MODULESDIR="${RC_LIBDIR}/net" +MODULESLIST="${RC_SVCDIR}/nettree" +_config_vars="config" + +[ -z "${IN_BACKGROUND}" ] && IN_BACKGROUND=false + +depend() { + local IFACE=${SVCNAME#*.} + local IFVAR=$(echo -n "${IFACE}" | sed -e 's/[^[:alnum:]]/_/g') + + need localmount + after bootmisc + provide net + case "${IFACE}" in + lo|lo0) ;; + *) + after net.lo net.lo0 + local prov= + eval prov=\$RC_NEED_${IFVAR} + [ -n "${prov}" ] && need ${prov} + eval prov=\$RC_USE_${IFVAR} + [ -n "${prov}" ] && use ${prov} + eval prov=\$RC_BEFORE_${IFVAR} + [ -n "${prov}" ] && before ${prov} + eval prov=\$RC_AFTER_${IFVAR} + [ -n "${prov}" ] && after ${prov} + eval prov=\$RC_PROVIDE_${IFVAR} + [ -n "${prov}" ] && provide ${prov} + ;; + esac +} + +_shell_var() { + echo -n "$1" | sed -e 's/[^[:alnum:]]/_/g' +} + +# Credit to David Leverton for this function which handily maps a bash array +# structure to positional parameters so existing configs work :) +# We'll deprecate arrays at some point though. +_get_array() { + if [ -n "${BASH}" ] ; then + case "$(declare -p "$1" 2>/dev/null)" in + "declare -a "*) + echo "set -- \"\${$1[@]}\"" + return + ;; + esac + fi + + echo "eval set -- \"\$$1\"" +} + +_wait_for_carrier() { + local timeout= efunc=einfon + + _has_carrier && return 0 + + eval timeout=\$carrier_timeout_${IF_VAR} + timeout=${timeout:-5} + + [ -n "${RC_EBUFFER}" ] && efunc=einfo + ${efunc} "Waiting for carrier (${timeout} seconds) " + while [ ${timeout} -gt 0 ] ; do + sleep 1 + if _has_carrier ; then + [ -z "${RC_EBUFFER}" ] && echo + return 0 + fi + timeout=$((${timeout} - 1)) + [ -z "${RC_EBUFFER}" ] && echo -n "." + done + + echo + return 1 +} + +_netmask2cidr() { + local i= len=0 + + local IFS=. + for i in $1; do + while [ ${i} != "0" ] ; do + len=$((${len} + 1)) + i=$((${i} >> 1)) + done + done + + echo "${len}" +} + +_configure_variables() { + local var= v= t= + + for var in ${_config_vars} ; do + local v= + for t in "$@" ; do + eval v=\$${var}_${t} + if [ -n "${v}" ] ; then + eval ${var}_${IFVAR}=\$${var}_${t} + continue 2 + fi + done + done +} + +_show_address() { + einfo "received address $(_get_inet_address "${IFACE}")" +} + +# Basically sorts our modules into order and saves the list +_gen_module_list() { + local x= f= + if [ -s "${MODULESLIST}" -a "${MODULESLIST}" -nt "${MODULESDIR}" ] ; then + local update=false + for x in "${MODULESDIR}"/* ; do + [ -e "${x}" ] || continue + if [ "${x}" -nt "${MODULESLIST}" ] ; then + update=true + break + fi + done + ${update} || return 0 + fi + + einfo "Caching network module dependencies" + # Run in a subshell to protect the main script + ( + after() { + eval ${MODULE}_after="\"\${${MODULE}_after}\${${MODULE}_after:+ }$*\"" + } + + before() { + local mod=${MODULE} + local MODULE= + for MODULE in "$@" ; do + after "${mod}" + done + } + + program() { + if [ "$1" = "start" -o "$1" = "stop" ] ; then + local s="$1" + shift + eval ${MODULE}_program_${s}="\"\${${MODULE}_program_${s}}\${${MODULE}_program_${s}:+ }$*\"" + else + eval ${MODULE}_program="\"\${${MODULE}_program}\${${MODULE}_program:+ }$*\"" + fi + } + + provide() { + eval ${MODULE}_provide="\"\${${MODULE}_provide}\${${MODULE}_provide:+ }$*\"" + local x + for x in $* ; do + eval ${x}_providedby="\"\${${MODULE}_providedby}\${${MODULE}_providedby:+ }${MODULE}\"" + done + } + + for MODULE in "${MODULESDIR}"/* ; do + sh -n "${MODULE}" || continue + . "${MODULE}" || continue + MODULE=${MODULE#${MODULESDIR}/} + MODULE=${MODULE%.sh} + eval ${MODULE}_depend + MODULES="${MODULES} ${MODULE}" + done + + VISITED= + SORTED= + visit() { + case " ${VISITED} " in + *" $1 "*) return ;; + esac + VISITED="${VISITED} $1" + + eval AFTER=\$${1}_after + for MODULE in ${AFTER} ; do + eval PROVIDEDBY=\$${MODULE}_providedby + if [ -n "${PROVIDEDBY}" ] ; then + for MODULE in ${PROVIDEDBY} ; do + visit "${MODULE}" + done + else + visit "${MODULE}" + fi + done + + eval PROVIDE=\$${1}_provide + for MODULE in ${PROVIDE} ; do + visit "${MODULE}" + done + + eval PROVIDEDBY=\$${1}_providedby + [ -z "${PROVIDEDBY}" ] && SORTED="${SORTED} $1" + } + + for MODULE in ${MODULES} ; do + visit "${MODULE}" + done + + > "${MODULESLIST}" + i=0 + for MODULE in ${SORTED} ; do + eval PROGRAM=\$${MODULE}_program + eval PROGRAM_START=\$${MODULE}_program_start + eval PROGRAM_STOP=\$${MODULE}_program_stop + #for x in ${PROGRAM} ; do + # [ -x "${x}" ] || continue 2 + #done + eval PROVIDE=\$${MODULE}_provide + echo "module_${i}='${MODULE}'" >> "${MODULESLIST}" + echo "module_${i}_program='${PROGRAM}'" >> "${MODULESLIST}" + echo "module_${i}_program_start='${PROGRAM_START}'" >> "${MODULESLIST}" + echo "module_${i}_program_stop='${PROGRAM_STOP}'" >> "${MODULESLIST}" + echo "module_${i}_provide='${PROVIDE}'" >> "${MODULESLIST}" + i=$((${i} + 1)) + done + echo "module_${i}=" >> "${MODULESLIST}" + ) + + return 0 +} + +_load_modules() { + # Ensure our list is up to date + _gen_module_list + + local starting=$1 mymods= + + MODULES= + if [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ] ; then + eval mymods=\$modules_${IFVAR} + [ -z "${mymods}" ] && mymods=${modules} + fi + + . "${MODULESLIST}" + local i=-1 x= mod= f= provides= + while true ; do + i=$((${i} + 1)) + eval mod=\$module_${i} + [ -z "${mod}" ] && break + [ -e "${MODULESDIR}/${mod}.sh" ] || continue + + eval set -- \$module_${i}_program + if [ -n "$1" ] ; then + x= + for x in "$@" ; do + [ -x "${x}" ] && break + done + [ -x "${x}" ] || continue + fi + if ${starting} ; then + eval set -- \$module_${i}_program_start + else + eval set -- \$module_${i}_program_stop + fi + if [ -n "$1" ] ; then + x= + for x in "$@" ; do + [ -x "${x}" ] && break + done + [ -x "${x}" ] || continue + fi + + eval provides=\$module_${i}_provide + if ${starting} ; then + case " ${mymods} " in + *" !${mod} "*) continue ;; + *" !${provides} "*) [ -n "${provides}" ] && continue ;; + esac + fi + MODULES="${MODULES}${MODULES:+ }${mod}" + + # Now load and wrap our functions + if ! . "${MODULESDIR}/${mod}.sh" ; then + eend 1 "${SVCNAME}: error loading module \`${mod}'" + exit 1 + fi + + [ -z "${provides}" ] && continue + + # Wrap our provides + local f= + for f in pre_start start post_start ; do + eval "${provides}_${f}() { type ${mod}_${f} >/dev/null 2>/dev/null || return 0; ${mod}_${f} \"\$@\"; }" + done + + eval module_${mod}_provides="${provides}" + eval module_${provides}_providedby="${mod}" + done + + # Wrap our preferred modules + for mod in ${mymods} ; do + case " ${MODULES} " in + *" ${mod} "*) + eval x=\$module_${mod}_provides + [ -z "${x}" ] && continue + for f in pre_start start post_start ; do + eval "${x}_${f}() { type ${mod}_${f} >/dev/null 2>/dev/null || return 0; ${mod}_${f} \"\$@\"; }" + done + eval module_${x}_providedby="${mod}" + ;; + esac + done + + # Finally remove any duplicated provides from our list if we're starting + # Otherwise reverse the list + local LIST="${MODULES}" p= + MODULES= + if ${starting} ; then + for mod in ${LIST} ; do + eval x=\$module_${mod}_provides + if [ -n "${x}" ] ; then + eval p=\$module_${x}_providedby + [ "${mod}" != "${p}" ] && continue + fi + MODULES="${MODULES}${MODULES:+ }${mod}" + done + else + for mod in ${LIST} ; do + MODULES="${mod}${MODULES:+ }${MODULES}" + done + fi + + veinfo "Loaded modules: ${MODULES}" +} + +_load_config() { + eval "$(_get_array "config_${IFVAR}")" + if [ "${IFACE}" = "lo" -o "${IFACE}" = "lo0" ] ; then + set -- "127.0.0.1/8" "$@" + else + if [ $# -eq 0 ] ; then + ewarn "No configuration specified; defaulting to DHCP" + set -- "dhcp" + fi + fi + + # We store our config in an array like vars + # so modules can influence it + config_index=0 + for cmd in "$@" ; do + eval config_${config_index}="'${cmd}'" + config_index=$((${config_index} + 1)) + done + # Terminate the list + eval config_${config_index}= + + config_index=0 + eval $(_get_array fallback_${IFVAR}) + for cmd in "$@" ; do + eval fallback_${config_index}="'${cmd}'" + config_index=$((${config_index} + 1)) + done + # Terminate the list + eval fallback_${config_index}= + + # Don't set to zero, so any net modules don't have to do anything extra + config_index=-1 +} + +start() { + local IFACE=${SVCNAME#*.} oneworked=false module= + local IFVAR=$(_shell_var "${IFACE}") cmd= metric=0 our_metric=$metric + + einfo "Bringing up interface ${IFACE}" + eindent + + if [ -z "${MODULES}" ] ; then + local MODULES= + _load_modules true + fi + + _up 2>/dev/null + + if type preup >/dev/null 2>/dev/null ; then + ebegin "Running preup" + eindent + preup || return 1 + eoutdent + fi + + for module in ${MODULES} ; do + if type "${module}_pre_start" >/dev/null 2>/dev/null ; then + if ! ${module}_pre_start ; then + eend 1 + exit 1 + fi + fi + done + + local config= config_index= + _load_config + config_index=0 + + if [ -n "${our_metric}" ] ; then + metric=${our_metric} + elif [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ] ; then + metric=$((${metric} + $(_ifindex))) + fi + + while true ; do + eval config=\$config_${config_index} + [ -z "${config}" ] && break + + set -- "${config}" + ebegin "$1" + eindent + case "$1" in + noop) + if [ -n "$(_get_inet_address)" ] ; then + oneworked=true + break + fi + ;; + null) : ;; + [0-9]*|*:*) _add_address ${config} ;; + *) + if type "${config}_start" >/dev/null 2>/dev/null ; then + "${config}"_start + else + eerror "nothing provides \`${config}'" + fi + ;; + esac + if eend $? ; then + oneworked=true + else + eval config=\$fallback_${IFVAR} + if [ -n "${config}" ] ; then + einfo "Trying fallback configuration" + eval config_${config_index}=\$fallback_${IFVAR} + eval fallback_${config_index}= + config_index=$((${config_index} - 1)) + fi + fi + eoutdent + config_index=$((${config_index} + 1)) + done + + if ! ${oneworked} ; then + if type failup >/dev/null 2>/dev/null ; then + ebegin "Running failup" + eindent + failup + eoutdent + fi + return 1 + fi + + local hidefirstroute=false first=true routes= + eval "$(_get_array "routes_${IFVAR}")" + if [ "${IFACE}" = "lo" -o "${IFACE}" = "lo0" ] ; then + set -- "127.0.0.0/8 via 127.0.0.1" "$@" + hidefirstroute=true + fi + for cmd in "$@" ; do + if ${first} ; then + first=false + einfo "Adding routes" + fi + eindent + ebegin "${cmd}" + # Work out if we're a host or a net if not told + case "${cmd}" in + *" -net "*|*" -host "*) ;; + *" netmask "*) cmd="-net ${cmd}" ;; + *) + case "${cmd%% *}" in + *.*.*.*/32) cmd="-host ${cmd}" ;; + *.*.*.*/*|0.0.0.0|default) cmd="-net ${cmd}" ;; + *) cmd="-host ${cmd}" ;; + esac + ;; + esac + if ${hidefirstroute} ; then + _add_route ${cmd} >/dev/null 2>/dev/null + hidefirstroute=false + else + _add_route ${cmd} >/dev/null + fi + eend $? + eoutdent + done + + for module in ${MODULES} ; do + if type "${module}_post_start" >/dev/null 2>/dev/null ; then + if ! ${module}_post_start ; then + eend 1 + exit 1 + fi + fi + done + + if type postup >/dev/null 2>/dev/null ; then + ebegin "Running postup" + eindent + postup + eoutdent + fi + + return 0 +} + +stop() { + local IFACE=${SVCNAME#*.} module= + local IFVAR=$(_shell_var "${IFACE}") opts= + + einfo "Bringing down interface ${IFACE}" + eindent + + if [ -z "${MODULES}" ] ; then + local MODULES= + _load_modules false + fi + + if type predown >/dev/null 2>/dev/null ; then + ebegin "Running predown" + eindent + predown || return 1 + eoutdent + fi + + for module in ${MODULES} ; do + if type "${module}_pre_stop" >/dev/null 2>/dev/null ; then + if ! ${module}_pre_stop ; then + eend 1 + exit 1 + fi + fi + done + + for module in ${MODULES} ; do + if type "${module}_stop" >/dev/null 2>/dev/null ; then + ${module}_stop + fi + done + + _delete_addresses "${IFACE}" + + for module in ${MODULES} ; do + if type "${module}_post_stop" >/dev/null 2>/dev/null ; then + ${module}_post_stop + fi + done + + [ "${IN_BACKGROUND}" != "true" ] && \ + [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ] && \ + _down 2>/dev/null + + [ -x /sbin/resolvconf ] && resolvconf -d "${IFACE}" + + if type predown >/dev/null 2>/dev/null ; then + ebegin "Running postdown" + eindent + postdown + eoutdent + fi + + return 0 +} + +# vim: set ts=4 : diff --git a/sh/rc-functions.sh b/sh/rc-functions.sh new file mode 100755 index 00000000..586471dd --- /dev/null +++ b/sh/rc-functions.sh @@ -0,0 +1,60 @@ +# Copyright 2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +has_addon() { + [ -e "${RC_LIBDIR}/addons/$1.sh" ] +} + +import_addon() { + if has_addon "$1" ; then + . "${RC_LIBDIR}/addons/$1.sh" + return 0 + fi + return 1 +} + +start_addon() { + ( import_addon "$1-start" ) +} + +stop_addon() { + ( import_addon "$1-stop" ) +} + +is_net_fs() { + [ -z "$1" ] && return 1 + + local t=$(mountinfo --fstype "$1" ) + for x in ${RC_NET_FS_LIST} ; do + [ "${x}" = "${t}" ] && return 0 + done + return 1 +} + +is_union_fs() { + [ ! -x /sbin/unionctl ] && return 1 + unionctl "$1" --list >/dev/null 2>/dev/null +} + +get_bootparam() { + local match="$1" + [ -z "${match}" -o ! -r /proc/cmdline ] && return 1 + + set -- $(cat /proc/cmdline) + while [ -n "$1" ] ; do + case "$1" in + gentoo=*) + local params="${1##*=}" + local IFS=, x= + for x in ${params} ; do + [ "${x}" = "${match}" ] && return 0 + done + ;; + esac + shift + done + + return 1 +} + +# vim: set ts=4 : diff --git a/sh/rc-help.sh b/sh/rc-help.sh new file mode 100755 index 00000000..97ca53f4 --- /dev/null +++ b/sh/rc-help.sh @@ -0,0 +1,262 @@ +#!/bin/sh +# Copyright 1999-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +if [ "${RC_NOCOLOR}" = "yes" ] ; then + unset BLUE GREEN OFF CYAN +else + BLUE="\033[34;01m" + GREEN="\033[32;01m" + OFF="\033[0m" + CYAN="\033[36;01m" +fi + +myscript=$1 +if [ -z "${myscript}" ] ; then + echo "Please execute an init.d script" + exit 1 +fi + +if [ -L "${myscript}" ] ; then + SERVICE=$(readlink "${myscript}") +else + SERVICE=${myscript} +fi +SERVICE=${SERVICE##*/} + +if [ "$2" = "help" ] ; then + BE_VERBOSE="yes" + NL="\n" +else + BE_VERBOSE="no" + NL= +fi + +default_opts="start stop restart pause zap" +extra_opts="$(. "${myscript}" 2>/dev/null ; echo "${opts}")" + +if [ "${BE_VERBOSE}" = "yes" ] ; then +printf " +${GREEN}Gentoo RC-Scripts; ${BLUE}http://www.gentoo.org/${OFF} + Copyright 1999-2007 Gentoo Foundation; Distributed under the GPL +" +fi + +printf "Usage: ${CYAN}${SERVICE}${OFF} [ ${GREEN}flags${OFF} ] < ${GREEN}options${OFF} > + +${CYAN}Normal Options:${OFF}" + +if [ "${BE_VERBOSE}" = "yes" ] ; then +printf " + ${GREEN}start${OFF} + Start service, as well as the services it depends on (if not already + started). + + ${GREEN}stop${OFF} + Stop service, as well as the services that depend on it (if not already + stopped). + + ${GREEN}restart${OFF} + Restart service, as well as the services that depend on it. + + Note to developers: If this function is replaced with a custom one, + 'svc_start' and 'svc_stop' should be used instead of 'start' and + 'stop' to restart the service. This is so that the dependencies + can be handled correctly. Refer to the portmap rc-script for an + example. + + ${GREEN}conditionalrestart|condrestart${OFF} + Same as 'restart', but only if the service has already been started. + + ${GREEN}pause${OFF} + Same as 'stop', but the services that depends on it, will not be + stopped. This is useful for stopping a network interface without + stopping all the network services that depend on 'net'. + + ${GREEN}zap${OFF} + Reset a service that is currently stopped, but still marked as started, + to the stopped state. Basically for killing zombie services. + + ${GREEN}status${OFF} + Prints \"status: started\" if the service is running, else it + prints \"status: stopped\". + + Note that if the '--quiet' flag is given, it will return true if the + service is running, else false. + + ${GREEN}ineed|iuse${OFF} + List the services this one depends on. Consult the section about + dependencies for more info on the different types of dependencies. + + ${GREEN}needsme|usesme${OFF} + List the services that depend on this one. Consult the section about + dependencies for more info on the different types of dependencies. + + ${GREEN}broken${OFF} + List the missing or broken dependencies of type 'need' this service + depends on. +" + +else + +printf " ${GREEN}${default_opts}${OFF} + Default init.d options. +" + +fi + +if [ -n "${extra_opts}" ] ; then +printf " +${CYAN}Additional Options:${OFF}${NL} + ${GREEN}${extra_opts}${OFF} + Extra options supported by this init.d script. +" +fi + +printf " +${CYAN}Flags:${OFF}${NL} + ${GREEN}--ifstarted${OFF} Only do actions if service started + ${GREEN}--nodeps${OFF} Don't stop or start any dependencies + ${GREEN}--quiet${OFF} + Suppress output to stdout, except if:${NL} + 1) It is a warning, then output to stdout + 2) It is an error, then output to stderr${NL} + ${GREEN}--verbose${OFF} Output extra information + ${GREEN}--debug${OFF} Output debug information + ${GREEN}--nocolor${OFF} Suppress the use of colors +" + +if [ "${BE_VERBOSE}" = "yes" ] ; then +printf " +${CYAN}Dependencies:${OFF} + + This is the heart of the Gentoo RC-Scripts, as it determines the order + in which services gets started, and also to some extend what services + get started in the first place. + + The following example demonstrates how to use dependencies in + rc-scripts: + + depend() { + need foo bar + use ray + } + + Here we have foo and bar as dependencies of type 'need', and ray of + type 'use'. You can have as many dependencies of each type as needed, as + long as there is only one entry for each type, listing all its dependencies + on one line only. + + ${GREEN}need${OFF} + These are all the services needed for this service to start. If any + service in the 'need' line is not started, it will be started even if it + is not in the current, or 'boot' runlevel, and then this service will be + started. If any services in the 'need' line fails to start or is + missing, this service will never be started. + + ${GREEN}use${OFF} + This can be seen as representing optional services this service depends on + that are not critical for it to start. For any service in the 'use' line, + it must be added to the 'boot' or current runlevel to be considered a + valid 'use' dependency. It can also be used to determine startup order. + + ${GREEN}before${OFF} + This, together with the 'after' dependency type, can be used to control + startup order. In core, 'before' and 'after' do not denote a dependency, + but should be used for order changes that will only be honoured during + a change of runlevel. All services listed will get started *after* the + current service. In other words, this service will get started *before* + all listed services. + + ${GREEN}after${OFF} + All services listed will be started *before* the current service. Have a + look at 'before' for more info. + + ${GREEN}provide${OFF} + This is not really a dependency type, rather it will enable you to create + virtual services. This is useful if there is more than one version of + a specific service type, system loggers or crons for instance. Just + have each system logger provide 'logger', and make all services in need + of a system logger depend on 'logger'. This should make things much more + generic. + + Note that the 'need', 'use', 'before', and 'after' dependency types accept + an '*' as an argument. Having: + + depend() { + before * + } + + will make the service start first in the current runlevel, and: + + depend() { + after * + } + + will make the service the last to start. + + You should however be careful how you use this, as I really will not + recommend using it with the 'need' or 'use' dependency type ... you have + been warned! + +${CYAN}'net' Dependency and 'net.*' Services:${OFF} + + Example: + + depend() { + need net + } + + This is a special dependency of type 'need'. It represents a state where + a network interface or interfaces besides lo is up and active. Any service + starting with 'net.' will be treated as a part of the 'net' dependency, + if: + + 1. It is part of the 'boot' runlevel + 2. It is part of the current runlevel + + A few examples are the /etc/init.d/net.eth0 and /etc/init.d/net.lo services. +" +fi + +printf " +${CYAN}Configuration files:${OFF} +" + +if [ "${BE_VERBOSE}" = "yes" ] ; then +printf " + There are two files which will be sourced for possible configuration by + the rc-scripts. They are (sourced from top to bottom): +" +fi + +printf " /etc/conf.d/${SERVICE}${NL} /etc/rc.conf" + +if [ "${BE_VERBOSE}" = "yes" ] ; then +printf " + You can add extra dependencies to ${SERVICE} by adding some variables to + /etc/conf.d/${SERVICE} + RC_NEED=\"openvpn ntpd\" + RC_USE=\"dns\" + + This makes ${SERVICE} need openvpn and ntpd, while it just uses dns. + + A good example of this is nfsmount needing openvpn if the nfs mounts in + /etc/fstab are over the vpn link. +" +fi + +if [ "${BE_VERBOSE}" = "yes" ] ; then +printf "\n +${CYAN}Management:${OFF} + + Services are added and removed via the 'rc-update' tool. Running it without + arguments should give sufficient help. +" +else +printf "\n +For more info, please run '${myscript} help'. +" +fi + +exit 0 diff --git a/sh/rc-mount.sh b/sh/rc-mount.sh new file mode 100644 index 00000000..67ea203f --- /dev/null +++ b/sh/rc-mount.sh @@ -0,0 +1,70 @@ +# Copyright 2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +# bool do_unmount(char *cmd, char *no_unmounts, char *nodes, char *fslist) +# Handy function to handle all our unmounting needs +# find-mount is a C program to actually find our mounts on our supported OS's +do_unmount() { + local cmd="$1" retval=0 retry= + local f_opts="-m -c" f_kill="-s " mnt= + if [ "${RC_UNAME}" = "Linux" ] ; then + f_opts="-c" + f_kill="-" + fi + + local mnts="$(mountinfo ${2:+--skip-regex} $2 ${3:+--node-regex} $3 ${4:+--fstype-regex} $4 --reverse \ + | sed -e "s/'/'\\\\''/g" -e "s/^/'/g" -e "s/$/'/g")" + eval set -- ${mnts} + for mnt in "$@" ; do + case "${cmd}" in + umount*) + # If we're using the mount (probably /usr) then don't unmount us + local pids="$(fuser ${f_opts} "${mnt}" 2>/dev/null)" + case " ${pids} " in + *" $$ "*) + ewarn "We are using ${mnt}, not unmounting" + continue + ;; + esac + ebegin "Unmounting ${mnt}" + ;; + *) + ebegin "Remounting ${mnt}" + ;; + esac + + retry=3 + while ! ${cmd} "${mnt}" 2>/dev/null ; do + # Don't kill if it's us (/ and possibly /usr) + local pids="$(fuser ${f_opts} "${mnt}" 2>/dev/null)" + case " ${pids} " in + *" $$ "*) retry=0 ;; + " ") eend 1 "in use but fuser finds nothing"; retry=0 ;; + *) + local sig="KILL" + [ ${retry} -gt 0 ] && sig="TERM" + fuser ${f_kill}${sig} -k ${f_opts} "${mnt}" \ + >/dev/null 2>/dev/null + sleep 1 + retry=$((${retry} - 1)) + ;; + esac + + # OK, try forcing things + if [ ${retry} -le 0 ] ; then + ${cmd} -f "${mnt}" || retry=-999 + retry=-999 + break + fi + done + if [ ${retry} -eq -999 ] ; then + eend 1 + retval=1 + else + eend 0 + fi + done + return ${retval} +} + +# vim: set ts=4 : diff --git a/sh/runscript.sh b/sh/runscript.sh new file mode 100755 index 00000000..6b679c21 --- /dev/null +++ b/sh/runscript.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# Shell wrapper for runscript +# Copyright 1999-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +. /etc/init.d/functions.sh +. "${RC_LIBDIR}"/sh/rc-functions.sh + +# Support LiveCD foo +if [ -r /sbin/livecd-functions.sh ] ; then + . /sbin/livecd-functions.sh + livecd_read_commandline +fi + +if [ -z "$1" -o -z "$2" ] ; then + eerror "${SVCNAME}: not enough arguments" + exit 1 +fi + +[ "${RC_DEBUG}" = "yes" ] && set -x + +# If we're net.eth0 or openvpn.work then load net or openvpn config +rc_c=${SVCNAME%%.*} +if [ -n "${rc_c}" -a "${rc_c}" != "${SVCNAME}" ] ; then + if [ -e "/etc/conf.d/${rc_c}.${RC_SOFTLEVEL}" ] ; then + . "/etc/conf.d/${rc_c}.${RC_SOFTLEVEL}" + elif [ -e "/etc/conf.d/${rc_c}" ] ; then + . "/etc/conf.d/${rc_c}" + fi +fi +unset rc_c + +# Overlay with our specific config +if [ -e "/etc/conf.d/${SVCNAME}.${RC_SOFTLEVEL}" ] ; then + . "/etc/conf.d/${SVCNAME}.${RC_SOFTLEVEL}" +elif [ -e "/etc/conf.d/${SVCNAME}" ] ; then + . "/etc/conf.d/${SVCNAME}" +fi + +# Load any system overrides +[ -e /etc/rc.conf ] && . /etc/rc.conf + +# Apply any ulimit defined +[ -n "${RC_ULIMIT}" ] && ulimit ${RC_ULIMIT} + +# Load our script +. $1 + +shift + +while [ -n "$1" ] ; do + # See if we have the required function and run it + for rc_x in start stop ${opts} ; do + if [ "${rc_x}" = "$1" ] ; then + if type "$1" >/dev/null 2>/dev/null ; then + unset rc_x + "$1" || exit $? + shift + continue 2 + else + if [ "${rc_x}" = "start" -o "${rc_x}" = "stop" ] ; then + exit 0 + else + eerror "${SVCNAME}: function \`$1' defined but does not exist" + exit 1 + fi + fi + fi + done + eerror "${SVCNAME}: unknown function \`$1'" + exit 1 +done + +# vim: set ts=4 : -- cgit v1.2.3