@ -32,8 +32,8 @@ fi
# program name, version and date
progname="rc"
progvers="1.1 .0"
progdate="09 -Jul-2003"
progvers="1.2 .0"
progdate="16 -Jul-2003"
# helper variables
NL="
@ -52,7 +52,6 @@ print=0
eval=0
config=0
query=0
raw=0
rcdir="@l_prefix@/etc/rc.d"
# iterate over argument line
@ -71,7 +70,6 @@ while [ $# -gt 0 ]; do
-e|--eval ) eval=1 ;;
-c|--config ) config=1 ;;
-q|--query ) query=1 ;;
-r|--raw ) raw=1 ;;
--rcdir=* ) rcdir=$arg ;;
-* ) help="Invalid option \`$opt'"; break ;;
* ) break ;;
@ -79,31 +77,13 @@ while [ $# -gt 0 ]; do
shift
done
# determine a reasonable default silent/verbose situation in case
# nothing was explicitly specified or a conflicting situation was
# specified. Else is silent set either disabled by default (0) or
# was explicitly enabled (1).
if [ $silent -eq $verbose ]; then
if [ -t 1 ]; then
# stdout connected to a terminal device
silent=0
else
# stdout NOT connected to a terminal device
silent=1
fi
fi
# determine path to rc.conf
rcconf="`echo $rcdir | sed -e 's;/rc.d$;/rc.conf;'`"
rcfunc="`echo $rcdir | sed -e 's;/rc.d$;/rc.func;'`"
# error or usage message
# display error or usage message
if [ ".$help" != .0 ]; then
if [ ".$help" != ".Usage" ]; then
echo "$progname:ERROR: $help" 1>&2
fi
echo "Usage: $progname [-s|--silent] [-d|--debug] [-h|--help]" 1>&2
echo " [-p|--print] [-e|--eval] [-c|--config] [-q|--query] [-r|--raw] " 1>&2
echo "Usage: $progname [-s|--silent] [-v|--verbose] [-d|--debug] [-h|--help]" 1>&2
echo " [-p|--print] [-e|--eval] [-c|--config] [-q|--query]" 1>&2
echo " <package> <command> [<command> ...]" 1>&2
if [ ".$help" != ".Usage" ]; then
exit 1
@ -112,46 +92,86 @@ if [ ".$help" != .0 ]; then
fi
fi
# extend run-time environment with our local tools (shtool, rpmtool, etc)
# determine a reasonable default silent/verbose situation in case
# nothing was explicitly specified or a conflicting situation was
# specified. Else is silent either disabled by default or was
# explicitly enabled.
if [ $silent -eq $verbose ]; then
if [ -t 2 ]; then
# stdout connected to a terminal device, so no need to be silent
silent=0
else
# stdout NOT connected to a terminal device, so be silent
silent=1
fi
fi
# determine path to rc.conf and rc.func
rcconf="`echo $rcdir | sed -e 's;/rc.d$;/rc.conf;'`"
rcfunc="`echo $rcdir | sed -e 's;/rc.d$;/rc.func;'`"
# extend run-time environment with local OpenPKG tools (shtool, rpmtool, etc)
PATH="@l_prefix@/bin:$PATH"
PATH="@l_prefix@/sbin:$PATH"
PATH="@l_prefix@/lib/openpkg:$PATH"
# set a reasonable temporary location
tmpdir="/tmp"
tmpfile="$tmpdir/rc.$$.tmp"
outfile="$tmpdir/rc.$$.out"
errfile="$tmpdir/rc.$$.err"
allfile="$tmpdir/rc.$$.all"
# establish secure temporary directory
i=0
while [ $i -lt 10 ]; do
tmpdir="${TMPDIR:-/tmp}/rc-`date '+%Y%m%d%H%M%S'`-$$"
(umask 077; mkdir $tmpdir) && break
i=`expr $i + 1`
sleep 1
done
if [ $i -eq 10 ]; then
echo "openpkg:rc:ERROR: unable to establish secure temporary directory" 1>&2
exit 1
fi
declare -r tmpdir
TMPDIR="$tmpdir"; export TMPDIR
TEMPDIR="$tmpdir"; export TEMPDIR
trap "trap - EXIT INT ABRT QUIT TERM; rm -rf $tmpdir >/dev/null 2>&1 || true" EXIT INT ABRT QUIT TERM
# determine reasonable temporary files
tmpfile="$tmpdir/rc.tmp"
outfile="$tmpdir/rc.out"
errfile="$tmpdir/rc.err"
allfile="$tmpdir/rc.all"
# handle --query option
if [ ".$query" = .1 ]; then
var="$1"
# suck in %config sections of all scripts for default values
scripts=`/bin/ls $rcdir/rc.* | sed -e "s;^$rcdir/rc\.;;"`
rm -f $tmpfile
touch $tmpfile
for s_name in $scripts; do
sed <$rcdir/rc.$s_name >>$tmpfile -e "1,/^%config/d" -e '/^%.*/,$d'
done
. $tmpfile
# apply override values
. $rcconf
eval "echo \${$var}"
rm -f $tmpfile
# display variable value
for var in $*; do
eval "echo \${$var}"
done
# stop processing immediately
exit 0
fi
# handle --config option
if [ ".$config" = .1 ]; then
# suck in %config sections of all scripts for default values
scripts=`/bin/ls $rcdir/rc.* | sed -e "s;^$rcdir/rc\.;;"`
rm -f $tmpfile
touch $tmpfile
for s_name in $scripts; do
sed <$rcdir/rc.$s_name >>$tmpfile -e "1,/^%config/d" -e '/^%.*/,$d'
done
vars=""
. $tmpfile
# remember default values
vars=""
OIFS="$IFS"; IFS="$NL"
for assign in `egrep '[ ]*[a-zA-Z_][a-zA-Z_0-9]*=' $tmpfile | sort`; do
var=`echo "$assign" | sed -e 's;^[ ]*\([a-zA-Z_][a-zA-Z_0-9]*\)=.*;\1;'`
@ -159,14 +179,21 @@ if [ ".$config" = .1 ]; then
eval "${var}_def=\"\$$var\""
done
IFS="$OIFS"
# apply override values to get effective values
. $rcconf
if [ ".$raw" = .0 ]; then
# determine how to print in bold mode in case output
# is connected to a terminal device
if [ -t 1 ]; then
begin_bold=`@l_prefix@/lib/openpkg/shtool echo -e '%B'`
end_bold=`@l_prefix@/lib/openpkg/shtool echo -e '%b'`
else
begin_bold=""
end_bold=""
fi
# iterate over all variables and display name, default and effective value
echo "${begin_bold}Configuration Variable Effective Value Default Value${end_bold}"
echo "------------------------ ------------------------- -- -------------------------"
for var in . $vars; do
@ -184,11 +211,12 @@ if [ ".$config" = .1 ]; then
echo dummy | awk '{ printf("%s%-24s %-25s %s %-25s%s\n", begin, var, val, tag, def, end); }' \
begin="$begin" var="$var" tag="$tag" val="\"$val\"" def="\"$def\"" end="$end"
done
rm -f $tmpfile
# stop processing immediately
exit 0
fi
# determine script(s) to use
# determine script(s) to use and make sure they exist
if [ $# -lt 1 ]; then
echo "openpkg:rc:ERROR: no package and command(s) specified" 1>&2
exit 1
@ -204,6 +232,7 @@ if [ ".$scripts" = ".all" ]; then
isall=1
. $rcconf
if [ ".$openpkg_runall" != . ]; then
# backward compatibility only
echo "openpkg:rc:WARNING: variable \"openpkg_runall\" was renamed to \"openpkg_rc_all\"." 1>&2
echo "openpkg:rc:WARNING: value of deprecated variable \"openpkg_runall\" taken over for compatibility." 1>&2
echo "openpkg:rc:WARNING: please update your local configuration in \"$rcconf\"." 1>&2
@ -221,38 +250,28 @@ else
fi
# determine current run-time user
user="`(id -un) 2>/dev/null`"
user=`(id -un) 2>/dev/null ||\
(id | sed -e 's;^[^(]*(\([^)]*\)).*;\1;') 2>/dev/null ||\
(whoami) 2>/dev/null ||\
(who am i | cut "-d " -f1) 2>/dev/null ||\
echo ${LOGNAME:-${USER}}`
if [ ".$user" = . ]; then
user="`(whoami) 2>/dev/null | awk '{ printf("%s", $1); }'`"
if [ ".$user" = . ]; then
user="`(who am i) 2>/dev/null | awk '{ printf("%s", $1); }'`"
if [ ".$user" = . ]; then
user="$LOGNAME"
fi
if [ ".$user" = . ]; then
if [ ".$user" = . ]; then
user="$USER"
if [ ".$user" = . ]; then
echo "openpkg:rc:ERROR: unable to determine current username" 1>&2
exit 1
fi
fi
exit 1
fi
fi
echo "openpkg:rc:ERROR: unable to determine current username" 1>&2
exit 1
fi
# iterate over the commands
# iterate over the specified commands
rv=0
cmds="$*"
for cmd in $cmds; do
# create "all outputs" file for execution operation (i.e. not --print and --eval)
if [ ".$print" = .0 -a ".$eval" = .0 ]; then
rm -f $allfile
touch $allfile
fi
# find scripts which contain the command and determine
# their individual user/prio settings
# their individual user/priority settings
list=''
for s_name in $scripts; do
enable=yes
@ -265,7 +284,7 @@ for cmd in $cmds; do
fi
fi
# check script options
# parse global script options
# (currently unused in OpenPKG)
shebangline=`head -1 $rcdir/rc.$s_name | grep "^#!rc"`
if [ ".$shebangline" != . ]; then
@ -286,20 +305,21 @@ for cmd in $cmds; do
case $opt in
-e|--enable ) enable=yes ;;
-d|--disable ) enable=no ;;
* ) echo "openpkg:rc:WARNING: invalid option \"$opt\" in \"$rcdir/rc.$s_name:#!rc\""; break ;;
* ) echo "openpkg:rc:WARNING: invalid global option \"$opt\" in \"$rcdir/rc.$s_name:#!rc\""; break ;;
esac
shift
done
fi
# check whether command exists in script
# check whether command exists in script at all
cmdline=`grep "^%$cmd" $rcdir/rc.$s_name`
if [ ".$cmdline" != . ]; then
# parse local command options
cmdopts=`echo "$cmdline" | sed -e "s;^%$cmd *;;"`
s_user=$user
s_prio=500
s_output=no
set -- $cmdopts;
set -- $cmdopts
prev=''
for opt
do
@ -319,26 +339,37 @@ for cmd in $cmds; do
-o|--output ) s_output=yes ;;
-u*|--user=* ) s_user=$arg ;;
-p*|--prio=* ) s_prio=$arg ;;
* ) echo "openpkg:rc:WARNING: invalid option \"$opt\" in \"$rcdir/rc.$s_name:%$cmd\""; break ;;
* ) echo "openpkg:rc:WARNING: invalid local option \"$opt\" in \"$rcdir/rc.$s_name:%$cmd\""; break ;;
esac
shift
done
# sanity check: is operation supported by current environment?
if [ ".$s_user" != ".$user" -a ".$user" != ".root" ]; then
echo "openpkg:rc:ERROR: $s_name:%$cmd: require root privileges to run as user \"$s_user\"" 1>&2
exit 1
fi
# skip script if disabled
# skip this script if script is disabled
if [ ".$enable" != .yes ]; then
continue
fi
# accumulate the determined information
list="$list,$s_prio:$s_name:$s_user:$s_output"
else
# command not found in script
if [ ".$isall" = .0 ]; then
echo "openpkg:rc:ERROR: $s_name: command \"$cmd\" not found" 1>&2
exit 1
fi
fi
done
# if operating on all scripts, complain if a non-standard command
# was used and it was not found in any(!) script at all. The
# standard commands are accepted to perform no operation if no
# packages are currently installed which provide such commands.
if [ ".$list" = . -a ".$isall" = .1 ]; then
case "$cmd" in
start|stop|monthly|weekly|daily|hourly|quarterly )
@ -351,121 +382,185 @@ for cmd in $cmds; do
esac
fi
# execute/print the scripts in order
# generate global (loop invariant) header for script in case of
# --print and --eval (for the execution approach we cannot do
# this, because there a new script is generated from scratch for
# each package.
if [ ".$print" = .1 -o ".$eval" = .1 ]; then
rm -f $tmpfile
touch $tmpfile
# generate: optionally enable shell debugging
if [ ".$debug" = .1 ]; then
echo "set -x" >>$tmpfile
fi
# generate: inclusion of the run-command helper functions
echo ". $rcfunc" >>$tmpfile
# generate: all %config sections of all(!) scripts. We cannot
# just include those which have the current command in it
# because by design all command scripts see the %config
# section of all(!) scripts. Because of $openpkg_rc_def the
# variable, we place the %config section of "openpkg" to the front.
l_scripts=`/bin/ls $rcdir/rc.* | sed -e "s;^$rcdir/rc\.;;" | egrep -v '^openpkg$' | sort`
l_scripts="openpkg $l_scripts"
for l_name in $l_scripts; do
sed <$rcdir/rc.$l_name >>$tmpfile -e "1,/^%config/d" -e '/^%.*/,$d'
done
# generate: inclusion of the application of override variables
echo ". $rcconf" >>$tmpfile
fi
verbose_first=1
verbose_output=0
# iterate over all packages (in priority order!) where the command
# iwas found n order to execute, print, or evaluate their scripts
verbose_pos=0
for entry in `echo $list | tr ',' '\012' | sort -n`; do
test ".$entry" = . && continue
[ ".$entry" = . ] && continue
# re-determine the script name, script and whether to print output
eval `echo $entry | sed -e 's%^[0-9]*:\(.*\):\(.*\):\(.*\)$%s_name="\1"; s_user="\2"; s_output="\3";%'`
if [ ".$print" = .0 -a ".$eval" = .0 ]; then
# display verbose progress message parts
if [ ".$print" = .0 -a ".$eval" = .0 -a ".$silent" = .0 ]; then
# line break if we already have output more than 70
# characters (notice that usually already more characters
# where printed, because of the name of the last script)
if [ $verbose_pos -gt 70 ]; then
verbose_pos=0
verbose_first=1
echo "" 1>&2
fi
if [ ".$verbose_first" = .1 ]; then
verbose_first=0
if [ ".$silent" != .1 ]; then
verbose_output=1
echo . | awk '{ printf("OpenPKG: %s: ", cmd); }' cmd="$cmd" 1>&2
l=`echo . | awk '{ printf("OpenPKG: %s: ", cmd); }' cmd="$cmd" | wc -c | awk '{ print $1; }'`
verbose_pos=`expr $verbose_pos + $l`
fi
# display verbose message parts: prefix (on first), separator and package
if [ $verbose_pos -eq 0 ]; then
echo . | awk '{ printf("OpenPKG: %s: ", cmd); }' cmd="$cmd" 1>&2
l=`echo . | awk '{ printf("OpenPKG: %s: ", cmd); }' cmd="$cmd" | wc -c | awk '{ print $1; }'`
verbose_pos=`expr $verbose_pos + $l`
prefix=""
else
prefix=", "
fi
if [ ".$silent" != .1 ]; then
echo . | awk '{ printf("%s%s", prefix, s_name); }' prefix="$prefix" s_name="$s_name" 1>&2
l=`echo . | awk '{ printf("%s%s", prefix, s_name); }' prefix="$prefix" s_name="$s_name" | wc -c | awk '{ print $1; }'`
verbose_pos=`expr $verbose_pos + $l`
fi
fi
if [ ".$debug" = .1 ]; then
echo "openpkg:rc:DEBUG: executing \"$rcdir/rc.$s_name:%$cmd\" as user \"$s_user\"" 1>&2
echo . | awk '{ printf("%s%s", prefix, s_name); }' prefix="$prefix" s_name="$s_name" 1>&2
l=`echo . | awk '{ printf("%s%s", prefix, s_name); }' prefix="$prefix" s_name="$s_name" | wc -c | awk '{ print $1; }'`
verbose_pos=`expr $verbose_pos + $l`
fi
# now operate on the particular script
if [ ".$print" = .1 -o ".$eval" = .1 ]; then
# special case: under --print and --eval we just add the
# %common and command scripts to the generated output script
# and do not execute anything at this point.
sed <$rcdir/rc.$s_name >>$tmpfile -e "1,/^%common/d" -e '/^%.*/,$d'
sed <$rcdir/rc.$s_name >>$tmpfile -e "1,/^%$cmd/d" -e '/^%.*/,$d'
continue
fi
rm -f $tmpfile $outfile $errfile
touch $tmpfile $outfile $errfile
if [ ".$debug" = .1 ]; then
echo "set -x" >>$tmpfile
fi
echo ". $rcfunc" >>$tmpfile
l_scripts=`/bin/ls $rcdir/rc.* | sed -e "s;^$rcdir/rc\.;;" | egrep -v '^openpkg$' | sort`
l_scripts="openpkg $l_scripts"
for l_name in $l_scripts; do
sed <$rcdir/rc.$l_name >>$tmpfile -e "1,/^%config/d" -e '/^%.*/,$d'
done
echo ". $rcconf" >>$tmpfile
sed <$rcdir/rc.$s_name >>$tmpfile -e "1,/^%common/d" -e '/^%.*/,$d'
sed <$rcdir/rc.$s_name >>$tmpfile -e "1,/^%$cmd/d" -e '/^%.*/,$d'
sh="@l_prefix@/lib/openpkg/bash"
if [ ".$user" != ".$s_user" ]; then
su - $s_user -c "PATH=\"$PATH\"; $sh $tmpfile" >$outfile 2>$errfile
rc=$?
else
$sh $tmpfile >$outfile 2>$errfile
rc=$?
fi
if [ $rc -ne 0 ]; then
if [ ".$silent" != .1 ]; then
echo ":FAILED" 1>&2
# the regular case of executing the command script directly
# prepare temporary files
rm -f $tmpfile $outfile $errfile
touch $tmpfile $outfile $errfile
# generate: optionally enable shell debugging
if [ ".$debug" = .1 ]; then
echo "set -x" >>$tmpfile
fi
echo "openpkg:rc:WARNING: @l_prefix@:$s_name:%$cmd: failed with return code $rc" 1>&2
if [ ".`cat $outfile $errfile`" != . ]; then
( echo "+----Log:--------------------------------------------------------------"
cat $outfile $errfile | sed -e 's;^;| ;'
echo "+----------------------------------------------------------------------"
) 1>&2
# generate: inclusion of the run-command helper functions
echo ". $rcfunc" >>$tmpfile
# generate: all %config sections of all(!) scripts. We cannot
# just include those which have the current command in it
# because by design all command scripts see the %config
# section of all(!) scripts. Because of $openpkg_rc_def the
# variable, we place the %config section of "openpkg" to the front.
l_scripts=`/bin/ls $rcdir/rc.* | sed -e "s;^$rcdir/rc\.;;" | egrep -v '^openpkg$' | sort`
l_scripts="openpkg $l_scripts"
for l_name in $l_scripts; do
sed <$rcdir/rc.$l_name >>$tmpfile -e "1,/^%config/d" -e '/^%.*/,$d'
done
# generate: inclusion of the application of override variables
echo ". $rcconf" >>$tmpfile
# generate: %common section and particular command section
sed <$rcdir/rc.$s_name >>$tmpfile -e "1,/^%common/d" -e '/^%.*/,$d'
sed <$rcdir/rc.$s_name >>$tmpfile -e "1,/^%$cmd/d" -e '/^%.*/,$d'
# execute the generated script with GNU Bash
sh="@l_prefix@/lib/openpkg/bash"
if [ ".$user" != ".$s_user" ]; then
# execute as different user
su - $s_user -c "PATH=\"$PATH\"; $sh $tmpfile" >$outfile 2>$errfile
rc=$?
else
# execute as current user
$sh $tmpfile >$outfile 2>$errfile
rc=$?
fi
verbose_first=1
verbose_pos=0
rv=1
else
if [ ".$s_output" = .yes ]; then
cat $outfile >>$allfile
if [ $rc -ne 0 ]; then
if [ ".$silent" = .0 ]; then
# indicate failure of execution on verbose message line
echo ":FAILED" 1>&2
verbose_pos=0
fi
# give details of execution failure
( echo "openpkg:rc:WARNING: @l_prefix@:$s_name:%$cmd: failed with return code $rc" 1>&2
if [ ".`cat $outfile $errfile`" != . ]; then
echo "+----Log:--------------------------------------------------------------"
cat $outfile $errfile | sed -e 's;^;| ;'
echo "+----------------------------------------------------------------------"
fi
) 1>&2
# enforce global return value
rv=1
else
if [ ".$s_output" = .yes ]; then
# accumulate script output for later display
cat $outfile >>$allfile
fi
fi
fi
done
if [ ".$print" = .1 -o ".$eval" = .1 ]; then
if [ ".$print" = .1 ]; then
cat $tmpfile
elif [ ".$eval" = .1 ]; then
echo ". $tmpfile; rm -f $tmpfile 2>/dev/null || true"
fi
# post-processing for each command
if [ ".$print" = .1 ]; then
# for --print just print the resulting script
cat $tmpfile
elif [ ".$eval" = .1 ]; then
# for --eval we cannot just print the resulting script because
# not all Bourne-Shell implementations like to "eval" large
# multi-line outputs. Hence we output a little one-liner which
# "sources" the script instead and cleans up. To make sure the
# "rm -rf $tmpdir" is not run by the automatic cleanup code,
# remove the EXIT trap.
echo ". $tmpfile; rm -rf $tmpdir 2>/dev/null || true"
else
if [ ".$silent" != .1 -a ".$verbose_output" = .1 -a $verbose_pos -gt 0 ]; then
# for the execution situation just make sure we
# terminate the verbose message output.
if [ ".$silent" = .0 -a $verbose_pos -gt 0 ]; then
echo "." 1>&2
fi
# additionally, if a script wants its output to be displayed,
# now do it. In case there was no such request, do not bother
# -- we then just output an empty file and so can avoid an
# extra test surrounding the next command.
cat $allfile
fi
done
# cleanup temporary files
# cleanup temporary files except the result script in
# case of --eval (which is then removed by the caller).
rm -f $outfile $errfile $allfile >/dev/null 2>&1 || true
if [ ".$eval" = .0 ]; then
rm -f $tmpfile >/dev/null 2>&1 || true
rm -rf $tmpdir >/dev/null 2>&1 || true
fi
# exit gracefully with global return value
# now clean the exit trap and exit with the global return value
# indicating to the caller whether all scripts were executed
# successfully or at least one failed.
trap - EXIT
exit $rv