postrun revision 8105
10139N/A#!/bin/ksh
10139N/A#
10139N/A# Script for starting a postponed post-installation command in
12144N/A# a Live-Upgrade-safe environment
10139N/A#
10139N/A# CDDL HEADER START
10139N/A#
12352N/A# The contents of this file are subject to the terms of the
10139N/A# Common Development and Distribution License, Version 1.0 only
10139N/A# (the "License"). You may not use this file except in compliance
10139N/A# with the License.
10139N/A#
15630N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10139N/A# or http://www.opensolaris.org/os/licensing.
10139N/A# See the License for the specific language governing permissions
10139N/A# and limitations under the License.
10139N/A#
15448N/A# When distributing Covered Code, include this CDDL HEADER in each
13577N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
13034N/A# If applicable, add the following below this CDDL HEADER, with the
15566N/A# fields enclosed by brackets "[]" replaced with your own identifying
15566N/A# information: Portions Copyright [yyyy] [name of copyright owner]
15630N/A#
15630N/A# CDDL HEADER END
10139N/A#
10139N/A#
10139N/A# Copyright 2004-2005 Sun Microsystems, Inc. All rights reserved.
10139N/A# Use is subject to license terms.
10139N/A#
10139N/A
10139N/Aexport PATH=/usr/bin
10139N/ALC_ALL=C
10139N/Aexport LC_ALL
10139N/AMYDIR=$(cd $(dirname $0); pwd)
10139N/ASPOOLDIR="$MYDIR/../../var/spool/postrun"
10139N/ALOCKFILE="$SPOOLDIR/.lock"
10139N/ALOGFILE="$MYDIR/../../var/log/postrun.log"
10139N/ASEQFILE="$SPOOLDIR/.seq"
10139N/A
10139N/Aid | grep " euid=" && \
10139N/A myuid=`id | sed -e 's/.* euid=\([0-9][0-9]*\).*$/\1/'` || \
10139N/A myuid=`id | sed -e 's/uid=\([0-9][0-9]*\).*/\1/'`
10139N/A
10139N/Aif [ "$myuid" != 0 ]; then
10139N/A echo "postrun: error: run this script as root"
10139N/A exit 1
13034N/Afi
15162N/A
15630N/Aumask 0133
10139N/A
10139N/Ausage() {
10139N/A echo 'Usage: postrun [options]'
10139N/A echo
10139N/A echo 'Options:'
10139N/A echo ' -u, --uniq'
10139N/A echo ' If the same command is requested multiple times, the command'
10139N/A echo ' is only run once. If it is safe to execute the command'
10139N/A echo ' immediately, it will be delayed by 5 minutes, or as set'
10139N/A echo ' using the --timeout option'
10139N/A echo
10139N/A echo ' -t <n>, --timeout <n>'
10139N/A echo ' Delay the execution of uniq commands by <n> minutes.'
10139N/A echo
10139N/A echo ' -b, --bg'
10139N/A echo ' Run the command in the background and return control'
12268N/A echo ' immediately'
12268N/A echo
12268N/A echo ' -f <file>'
12271N/A echo ' Read the commands from <file> instead of the standard'
13768N/A echo ' input.'
10778N/A echo
13768N/A echo ' -h, -?, --help'
15162N/A echo ' Display this help'
15162N/A exit 1
15162N/A}
10139N/A
10139N/Apostrun_debug() {
10139N/A if [ "x$POSTRUN_DEBUG" = xyes ]; then
10139N/A for msg in "${@}"; do
13360N/A echo '<POSTRUN_DEBUG>' "$msg" 1>&2
10139N/A done
10139N/A fi
10139N/A}
12649N/A
11370N/A#LOCK_UNLOCK_FUNCTIONS_START
12150N/Ais_number() {
12108N/A echo "$1" | egrep -vs '^[0-9]+$' && return 1
12108N/A echo "$1" | egrep -s '^[0-9]+$' || return 1
12500N/A return 0
12500N/A}
15162N/A
15162N/A# lock the postrun spool or log file
10139N/A# if $1 is 'log' then lock the log file, otherwise log the spool
10139N/Apostrun_lock() {
10139N/A this_lock=$LOCKFILE
10139N/A if [ "x$1" = xlog ]; then
10139N/A this_lock=${LOCKFILE}.log
10139N/A fi
10139N/A # lock file exists (contains the pid of the process that locked it)
10139N/A
10139N/A while test -f $this_lock; do
10139N/A # get the pid that holds the lock
10139N/A pid=`cat $this_lock 2>/dev/null` || continue
10139N/A # is the lock held by this process?
10424N/A test "$pid" == "$$" && return
10139N/A # check if the process is still running or else it's a stale lock
10139N/A is_number "$pid" && {
10139N/A ps -g $pid -o 'pid' | egrep -s "$pid\$" 2>&1 || {
10139N/A sleep 1
10139N/A postrun_lock $1
10139N/A return
10139N/A }
10139N/A }
10139N/A # we have a stale lock situation, let's try and take ownership
10139N/A # of the lock
10139N/A chmod 644 ${this_lock}
10139N/A echo "$$" > ${this_lock}
10139N/A chmod 444 ${this_lock}
10139N/A # if we successfully took ownership of the lock, this loop
10139N/A # will read our own pid back and return
10139N/A # otherwise
10139N/A done
10139N/A# FIXME ends
10139N/A # create a read-only lock file
10139N/A umask 0333
10139N/A # run false so that we enter the while loop
10139N/A false
10139N/A while [ $? != 0 ]; do
10139N/A # write the pid to the lock file
10139N/A # recurse if failed. recursion here helps to avoid an infinite
10139N/A # loop -- ksh will kill the script after 128 attempts
10139N/A echo "$$" > $this_lock || postrun_lock $1
10139N/A # read it back in case another process also wrote its pid there
10139N/A # in the meantime
10139N/A pid=`cat $this_lock 2>/dev/null`
10139N/A # the loop will restart is the file cannot be read
10139N/A done
10139N/A umask 0133
10139N/A # check if this process holds the lock or else try the whole thing again
10139N/A test "$pid" == "$$" || postrun_lock $1
10139N/A}
10139N/A
10139N/A# release the lock
10139N/A# unlock the log file if $1 == 'log', unlock the spool otherwise
10139N/Apostrun_unlock() {
10139N/A this_lock=$LOCKFILE
10139N/A if [ "x$1" = xlog ]; then
10139N/A this_lock=${LOCKFILE}.log
10139N/A fi
10139N/A if ! rm -f $this_lock; then
10139N/A echo "postrun: error: cannot remove lock file $this_lock"
10139N/A exit 1
10139N/A fi
10139N/A}
10139N/A#LOCK_UNLOCK_FUNCTIONS_END
10139N/A
10139N/A# get the next job id
10139N/Apostrun_get_seq() {
10139N/A postrun_lock
10139N/A seq=`cat $SEQFILE 2>/dev/null`
10139N/A next_seq=$(($seq + 1))
10139N/A echo $next_seq > $SEQFILE
10139N/A postrun_unlock
10139N/A echo $next_seq
15630N/A}
15630N/A
15566N/Apostrun_spool_command() {
15566N/A cd $SPOOLDIR
15566N/A # check if there's already a spooled job for the same command
15448N/A uniq_job_nr=
15448N/A IFS=' '
15162N/A postrun_lock
15162N/A for f in *.cmd; do
14555N/A test -f "$f" || continue
14555N/A cmp -s $postrun_command_file $f && {
14265N/A if [ $postrun_is_uniq = yes ]; then
14555N/A uniq_job_nr=`basename $f .cmd`
13936N/A break
13936N/A fi
13766N/A egrep -s '^uniq_command: yes' `basename $f .cmd`.ctrl && {
13768N/A uniq_job_nr=`basename $f .cmd`
13610N/A break
13610N/A }
13424N/A }
13424N/A done
13360N/A if [ "x$uniq_job_nr" != x ]; then
13360N/A postrun_debug "matching spooled uniq job (#${uniq_job_nr}) found"
13314N/A # we found a matching spooled uniq job
13314N/A # all we need to do is update the uniq time and make sure it's
13264N/A # flagged as a uniq job
13264N/A
13114N/A #
13114N/A # FIXME: shouldn't use sed for this, safer to simply append
13034N/A # and process the duplicate entries when reading the file
13034N/A #
12808N/A sed -e 's/^uniq_command: .*/uniq_command: yes/' \
12809N/A -e 's/^\(pkginst: .*\)/\1, '$PKGINST'/' \
12649N/A -e 's/^uniq_time: .*/uniq_time: '`date +%Y.%m.%d.%H.%M.%S`'/' \
12649N/A -e 's/^uniq_timeout: .*/uniq_timeout: '"$postrun_uniq_timeout"'/' \
12649N/A $uniq_job_nr.ctrl > $uniq_job_nr.ctrl.new
12586N/A
12586N/A #
12500N/A # FIXME: add the user name to the contol file
12500N/A #
12500N/A
12467N/A mv $uniq_job_nr.ctrl.new $uniq_job_nr.ctrl
12467N/A # run the spooled jobs in postrun_uniq_timeout minutes
12369N/A echo "$MYDIR/postrun -q" | \
12369N/A at now "+${postrun_uniq_timeout}minutes" > /dev/null 2>&1
12287N/A
12287N/A # FIXME: kill the previous at(1) job?
12268N/A
12268N/A else
12268N/A postrun_unlock
12268N/A job_seq=`postrun_get_seq`
12197N/A postrun_debug "spooling command as job #${job_seq}"
12197N/A postrun_lock
12171N/A user_name=${EMAIL:-root}
12171N/A ctrl_file="$SPOOLDIR/$job_seq.ctrl"
12171N/A cmd_file="$SPOOLDIR/$job_seq.cmd"
12145N/A cat $postrun_command_file > $cmd_file
12145N/A cat /dev/null > $ctrl_file
12144N/A echo "pkginst: $PKGINST" >> $ctrl_file
12144N/A echo "submit_time: `date +%Y.%m.%d.%H.%M.%S`" >> $ctrl_file
12108N/A echo "uniq_command: $postrun_is_uniq" >> $ctrl_file
12108N/A echo "uniq_time: `date +%Y.%m.%d.%H.%M.%S`" >> $ctrl_file
12108N/A echo "uniq_timeout: $postrun_uniq_timeout" >> $ctrl_file
12071N/A echo "background: $postrun_bg_job" >> $ctrl_file
12071N/A echo "user: $user_name" >> $ctrl_file
12022N/A fi
12022N/A postrun_unlock
11922N/A}
11922N/A
11819N/Apostrun_run_command() {
11819N/A cmdout=`mktemp /tmp/postrun.out.XXXX`
11416N/A # create a background jobs script that executes the commands
11416N/A # then locks the spool/log file and appends the output to the
11370N/A # log file and finally unlocks
11370N/A cmdfile=`mktemp /tmp/postrun.job.XXXX`
11370N/A cat /dev/null > $cmdfile
11256N/A cat /dev/null > $cmdout
11256N/A echo '#!/bin/ksh' >> $cmdfile
11172N/A # copy the postrun_lock and postrun_unlock commands from
11175N/A # this script to the background job script
11136N/A echo "LOCKFILE=$LOCKFILE" >> $cmdfile
11136N/A sed -e '1,/#LOCK_UNLOCK_FUNCTIONS_START/d' \
11123N/A -e '/#LOCK_UNLOCK_FUNCTIONS_END/,$d' $0 >> $cmdfile
11123N/A # save the stdout file description
11083N/A echo 'exec 3<&1' >> $cmdfile
11083N/A echo "exec >> $cmdout 2>&1" >> $cmdfile
11083N/A echo 'PATH=/usr/bin; export PATH' >> $cmdfile
11066N/A echo 'echo Starting postrun job at `LC_ALL=C date`' >> $cmdfile
11071N/A if [ "x$postrun_submit_time" != x ]; then
11071N/A if [ $postrun_bg_job = yes ]; then
11066N/A echo "echo 'This is a spooled background job (#${postrun_job_number})'" >> $cmdfile
10972N/A else
10972N/A echo "echo 'This is a spooled foreground job (#${postrun_job_number})'" >> $cmdfile
10972N/A fi
10820N/A echo "echo Job submitted by $postrun_pkginst at $postrun_submit_time" \
10820N/A >> $cmdfile
10778N/A else
10778N/A if [ $postrun_bg_job = yes ]; then
10778N/A echo 'echo This is an immediate background job' >> $cmdfile
10778N/A else
10774N/A echo 'echo This is an immediate foreground job' >> $cmdfile
10774N/A fi
10748N/A echo "echo Job submitted by $postrun_pkginst"\
10748N/A >> $cmdfile
10666N/A fi
10666N/A echo 'echo Running commands:' >> $cmdfile
10650N/A echo "echo '>>>' commands follow:" >> $cmdfile
10650N/A cat $postrun_command_file | \
10596N/A sed -e "s/'/'\"'\"'/g" | \
10596N/A sed -e "s/^/echo '/" \
10580N/A -e "s/\$/'/" >> $cmdfile
10580N/A echo "echo '<<<' commands end" >> $cmdfile
10473N/A echo "echo '>>>' Command output follows:" >> $cmdfile
10473N/A echo "chmod 700 $postrun_command_file" >> $cmdfile
10424N/A echo "$postrun_command_file" >> $cmdfile
10424N/A
10424N/A #
10346N/A # FIXME: send email to $postrun_user if the command failed
10346N/A #
10285N/A
10285N/A echo "echo '<<<' Command completed with exit status \$?" \
10179N/A >> $cmdfile
10187N/A echo 'echo Job finished at `LC_ALL=C date`' >> $cmdfile
10139N/A echo 'echo --' >> $cmdfile
10139N/A # restore PATH in case the command changed it
10139N/A echo 'PATH=/usr/bin; export PATH' >> $cmdfile
10139N/A # restore stdout
10139N/A echo 'exec 1<&3' >> $cmdfile
10139N/A # close file descriptor 3
10139N/A echo 'exec 3<&-' >> $cmdfile
10139N/A echo 'exec 2>&1' >> $cmdfile
10139N/A # append the messages to the real log file
10139N/A # need to lock the log file to avoid 2 postrun commands
10139N/A # writing at the same time and messing up the log
10139N/A echo 'postrun_lock log' >> $cmdfile
10139N/A echo "cat $cmdout >> $LOGFILE" >> $cmdfile
10139N/A echo 'postrun_unlock log' >> $cmdfile
10139N/A echo "rm -f $cmdout" >> $cmdfile
10139N/A echo "rm -f $cmdfile" >> $cmdfile
10139N/A echo "rm -f $postrun_command_file" >> $cmdfile
10139N/A chmod 700 $cmdfile
10139N/A if [ $postrun_bg_job = yes ]; then
10139N/A $cmdfile &
10139N/A else
10139N/A $cmdfile
10139N/A fi
10139N/A exitval=$?
10139N/A}
10139N/A
10139N/Ausername=${EMAIL:-root}
10139N/A
10139N/Apostrun_defaults() {
10139N/A # default settings
10139N/A postrun_pkginst="$PKGINST"
10139N/A postrun_submit_time=''
10139N/A postrun_uniq_time=''
10139N/A postrun_is_uniq=no
10139N/A postrun_uniq_timeout=5
10139N/A postrun_bg_job=no
10139N/A postrun_command_file=''
10139N/A postrun_user=${username}
10139N/A postrun_job_number='???'
10139N/A postrun_alt_root_okay=no
10139N/A}
10139N/A
10139N/A# usage: is_leap_year yyyy
10139N/Ais_leap_year() {
10139N/A cal 02 $1 | egrep -s 29 && return 0
10139N/A return 1
10139N/A}
10139N/A
10139N/A# get_abstime yy mm dd hh mm ss
10139N/A#
10139N/A# prints the elapsed time in seconds since 1970.01.01.00.00.00
10139N/A#Length of the months:
10139N/A# JA FE MA AP MY JN JL AU SE OC NO DE
10139N/Aset -A MONTH 0 31 28 31 30 31 30 31 31 30 31 30 31
10139N/Aget_abstime() {
10139N/A # the absolute time since 1970...
10139N/A t=0
10139N/A
10139N/A # number of years
10139N/A t=$(($t + ($1 - 1970) * 31536000))
10139N/A
10139N/A # add 1 day for each leap year
10139N/A y=1972
10139N/A end_y=$1
10139N/A if [ $2 -lt 2 ]; then
10139N/A end_y=$(($1 - 1))
10139N/A fi
10139N/A while [ $y -le $end_y ]; do
10139N/A is_leap_year $y && t=$(($t + 86400))
10139N/A y=$(($y + 4))
10139N/A done
10139N/A
10139N/A # number of months
10139N/A m=1
10139N/A while [ $m -lt $2 ]; do
10139N/A t=$(($t + ${MONTH[$m]} * 86400))
10139N/A m=$(($m + 1))
10139N/A done
10139N/A
10139N/A # number of days, hours, minutes and seconds:
10139N/A echo $(($t + ($3 - 1) * 86400 + $4 * 3600 + $5 * 60 + $6))
10139N/A}
10139N/A
10139N/A# get_timediff: prints the difference in seconds between 2 time strings
10139N/A# the time strings should be of the following format:
10139N/A# YYYY.MM.DD.HH.MM.SS as printed by date +%Y.%m.%d.%H.%M.%S
10139N/A#
10139N/A# Works for dates after 1970.01.01.00.00.00
10139N/A#
10139N/Aget_timediff() {
10139N/A year1=$(expr "$1" : "^\([^.]*\)\..*")
10139N/A month1=$(expr "$1" : "^[^.]*\.\([^.]*\)\..*")
10139N/A day1=$(expr "$1" : "^[^.]*\.[^.]*\.\([^.]*\)\..*")
10139N/A hour1=$(expr "$1" : "^[^.]*\.[^.]*\.[^.]*\.\([^.]*\)\..*")
10139N/A min1=$(expr "$1" : "^[^.]*\.[^.]*\.[^.]*\.[^.]*\.\([^.]*\)\..*")
10139N/A sec1=$(expr "$1" : "^[^.]*\.[^.]*\.[^.]*\.[^.]*\.[^.]*\.\([^.]*\)")
10139N/A
10139N/A year2=$(expr "$2" : "^\([^.]*\)\..*")
10139N/A month2=$(expr "$2" : "^[^.]*\.\([^.]*\)\..*")
10139N/A day2=$(expr "$2" : "^[^.]*\.[^.]*\.\([^.]*\)\..*")
10139N/A hour2=$(expr "$2" : "^[^.]*\.[^.]*\.[^.]*\.\([^.]*\)\..*")
10139N/A min2=$(expr "$2" : "^[^.]*\.[^.]*\.[^.]*\.[^.]*\.\([^.]*\)\..*")
10139N/A sec2=$(expr "$2" : "^[^.]*\.[^.]*\.[^.]*\.[^.]*\.[^.]*\.\([^.]*\)")
10139N/A
10139N/A # calculate seconds since 1970.01.01.00.00.00
10139N/A t1=`get_abstime $year1 $month1 $day1 $hour1 $min1 $sec1`
10139N/A t2=`get_abstime $year2 $month2 $day2 $hour2 $min2 $sec2`
10139N/A
10139N/A # print difference
10139N/A expr $t1 - $t2
10139N/A}
10139N/A
10139N/Apostrun_runq() {
10139N/A cd $SPOOLDIR
10139N/A IFS=' '
10139N/A postrun_lock
10139N/A for job in *.ctrl; do
10139N/A test -f "$job" || continue
10139N/A postrun_defaults
10139N/A while read var val; do
10139N/A case "$var" in
10139N/A pkginst: )
10139N/A postrun_pkginst="$val"
10139N/A ;;
10139N/A submit_time: )
10139N/A postrun_submit_time="$val"
10139N/A ;;
10139N/A uniq_command: )
10139N/A postrun_is_uniq="$val"
10139N/A ;;
10139N/A uniq_time: )
10139N/A postrun_uniq_time="$val"
10139N/A ;;
10139N/A uniq_timeout: )
10139N/A postrun_uniq_timeout="$val"
10139N/A ;;
10139N/A background: )
10139N/A postrun_bg_job="$val"
10139N/A ;;
10139N/A user: )
10139N/A postrun_user="$val"
10139N/A ;;
10139N/A * )
10139N/A echo "postrun: WARNING: invalid setting in $job: $var"
10139N/A ;;
10139N/A esac
10139N/A done < $job
10139N/A postrun_command_file=$SPOOLDIR/`basename $job .ctrl`.cmd
10139N/A postrun_job_number=`basename $job .ctrl`
10139N/A if [ $postrun_ignore_timeout = no ]; then
10139N/A # if it's a uniq job, check if it timed out
10139N/A if [ "x$postrun_is_uniq" = xyes ]; then
10139N/A # calculate time difference (seconds)
10139N/A tdiff=$(get_timediff $(date +%Y.%m.%d.%H.%M.%S) \
10139N/A $postrun_uniq_time)
10139N/A timeout_sec=$((postrun_uniq_timeout * 60))
10139N/A if [ $tdiff -ge $timeout_sec ]; then
10139N/A postrun_run_command
10139N/A rm -f $job
10139N/A fi
10139N/A else
10139N/A postrun_run_command
10139N/A rm -f $job
10139N/A fi
10139N/A else
10139N/A # ignore timeout, just run the job
10139N/A postrun_run_command
10139N/A rm -f $job
fi
done
postrun_unlock
exit 0
}
postrun_defaults
exitval=0
postrun_ignore_timeout=no
if [ $# = 1 -a "x$1" = 'x-qf' ]; then
# postrun-runq mode (ignore timeout for uniq jobs, since this is
# expected to be run at system boot)
postrun_ignore_timeout=yes
postrun_runq
exit 1
fi
if [ $# = 1 -a "x$1" = 'x-q' ]; then
# postrun-runq mode, to be run from at(1)
postrun_runq
exit 1
fi
# process the command line
while [ $# -gt 0 ]; do
case "$1" in
-h|-\?|--help)
usage
;;
-u|--uniq)
postrun_is_uniq=yes
;;
-b|--bg)
postrun_bg_job=yes
;;
-t|--timeout)
opt="$1"
if [ $# == 0 ]; then
echo "postrun: error: argument expected after $opt"
exit 1
fi
shift
timeout=$1
if ! is_number "$timeout"; then
echo "postrun: error: interger number expected after $opt (found \"$timeout\")"
exit 1
fi
postrun_uniq_timeout=$timeout
;;
-f)
opt="$1"
if [ $# == 0 ]; then
echo "postrun: error: argument expected after $opt"
exit 1
fi
shift
postrun_command_file="$1"
;;
-a)
postrun_alt_root_okay=yes
;;
--)
break
;;
*)
echo "postrun: error: invalid argument: $1"
exit 1
;;
esac
shift
done
if [ "x$postrun_command_file" = x ]; then
# save the standard input in a temporary file
tmp_cmd_file=`mktemp /tmp/postrun.cmd.XXXX`
cat > $tmp_cmd_file
postrun_command_file=$tmp_cmd_file
fi
if [ "$LUBIN" != "" ]; then
#
# Live Upgrade. Unsafe to run the command now.
# Put into spool and defer to next boot.
#
postrun_spool_command "${@}"
elif [ "$PKG_INSTALL_ROOT" != "" -a "$PKG_INSTALL_ROOT" != "/" -a \
"x$postrun_alt_root_okay" != xyes ]; then
#
# Installation to an alternate root directory
# Put command into spool and defer to next boot.
#
postrun_spool_command "${@}"
else
#
# Local package install. Everything's shiny happy,
# safe to run the command right now
#
if [ $postrun_is_uniq = yes ]; then
# don't run the command yet in case the same command is requested
# within the next postrun_uniq_timeout minutes
postrun_spool_command "${@}"
# run the spooled jobs in postrun_uniq_timeout minutes
echo "$MYDIR/postrun -q" | \
at now "+${postrun_uniq_timeout}minutes" > /dev/null 2>&1
else
postrun_debug "Executing commands immediately"
postrun_run_command "${@}"
# do not delete the tmp_cmd_file because it's the only copy of the
# commands (since the job is not spooled)
tmp_cmd_file=''
fi
fi
if [ "x$tmp_cmd_file" != x ]; then
rm -f $tmp_cmd_file
fi
exit $exitval