#!/usr/bin/ksh PROGNAME=`basename $0` ############################################################################### # # MULTI-SESSION BATCH LANGUAGE (COMBINED DAEMON, SUBMITTER, QUERY TOOL) # ############################################################################### ############################################################################### # # CONFIGURABLE STUFF # ############################################################################### SPOOL_DIR=/home/da/alexis/dev/bud/spool ############################################################################### # # CONFIGURABLE STUFF ENDS HERE # ############################################################################### # Set a load of important variables PATH=/bin:/sbin:/usr/bin:/usr/sbin:/etc:/usr/etc:/usr/local/bin:/usr/local/sbin VERBOSE_LEVEL=2 # verbosity level:3=info,warnings,errors TMP_DIR=/var/tmp # where to stash temporary files ERRORS_CAUSE_EXITS=true # error() calls without options do exits # $Id: dailybackup-hpux10.in,v 1.1 1998/05/05 16:29:06 alexis Exp $ VERSION="PATCHLEVEL_MARKER" # version id ############################################################################### # # MAIN FUNCTION (called from very bottom of script) # ############################################################################### main() { ########################################################################### # # PROCESS OPTIONS # ########################################################################### # default values for things modified by options MODE=submit TAPE_DEVICE=$TAPE FORCE=false debug 5 "before option processing" # process command line REAL options while [ "X$1" != X ]; do case $1 in -V) if expr "$VERSION" : 'P.*R$' > /dev/null; then warning "this is development version; use 'ident'" exit 1 else echo "$PROGNAME version $VERSION" exit 0 fi ;; -d) MODE=daemon ;; -q) MODE=query ;; -r) MODE=remove ;; -v) VERBOSE_LEVEL=3 ;; -debug) [ "X$2" == X ] && usage VERBOSE_LEVEL=$2 shift ;; -*) usage ;; *) break ;; esac shift done [ ! -d $SPOOL_DIR ] && error -e "spool directory $SPOOL_DIR does not exist!" [ ! -w $SPOOL_DIR ] && error -e "you do not have permission to access the spool directory" case $MODE in query) msbl_query "$@" ;; daemon) msbl_daemon "$@" ;; remove) msbl_remove "$@" ;; submit) msbl_submit "$@" ;; esac } msbl_remove() { JOB_ID=$1 lock $JOB_ID rm $SPOOL_DIR/$JOB_ID/{env,msbl,seq} unlock $JOB_ID rmdir $SPOOL_DIR/$JOB_ID } msbl_daemon_onejob() { JOB_ID=$1 INSTRUCTION_POINTER_FILE=$SPOOL_DIR/$JOB_ID/ip JOB_FILE=$SPOOL_DIR/$JOB_ID/msbl lock $JOB_ID if [ ! -f $INSTRUCTION_POINTER_FILE ]; then debug 5 "msbl_daemon_onejob: creating instruction pointer file" echo 1 > $INSTRUCTION_POINTER_FILE elif [ X`cat $INSTRUCTION_POINTER_FILE` = X ]; then error "invalid instruction pointer" fi grep -nh '^label' $JOB_FILE > $SPOOL_DIR/$JOB_ID/labels MAX_INSTRUCTION_POINTER=`wc -l < $JOB_FILE` while :; do INSTRUCTION_POINTER=`cat $INSTRUCTION_POINTER_FILE` debug 5 "msbl_daemon_onejob: instruction pointer: $INSTRUCTION_POINTER" if [ $INSTRUCTION_POINTER -gt $MAX_INSTRUCTION_POINTER ]; then debug 5 "msbl_daemon_onejob: reached end of job" rm -fr $SPOOL_DIR/$JOB_ID exit 0 fi INS=`sed -n "${INSTRUCTION_POINTER}p" < $JOB_FILE` debug 5 "msbl_daemon_onejob: instruction: $INS" INS=`expr $INSTRUCTION_POINTER + 1` echo $INS > $INSTRUCTION_POINTER_FILE done unlock $JOB_ID } msbl_daemon() { ( cd $SPOOL_DIR; ls ) | while read JOB_ID; do msbl_daemon_onejob $JOB_ID & done } msbl_query() { echo "job owner state" cd $SPOOL_DIR || error "can't access spool directory $SPOOL_DIR" ls -l | tail +2l | while read J1 J2 OWNER J4 J5 J6 J7 J8 JOB_ID; do STATE=running [ -f $SPOOL_DIR/$JOB_ID/wait ] && { STATE < $SPOOL_DIR/$JOB_ID/wait; } echo "$JOB_ID $OWNER $STATE" done } msbl_submit() { JOB_ID=`date '+%y%m%d%H%M%S'`.$$ mkdir $SPOOL_DIR/$JOB_ID 2>/dev/null || error -e "can't create job directory" lock $JOB_ID || error -e "can't lock job $JOB_ID" { cat $1 > $SPOOL_DIR/$JOB_ID/msbl; } 2>/dev/null || error -e "access problem" unlock $JOB_ID } ############################################################################### # # RESILIANT MESSAGING FUNCTIONS # ############################################################################### usage() { { echo "Usage: $PROGNAME [ -d | -q | -s | -r ] [ ]" echo " $PROGNAME -V" } >&2 exit 1 } internal() { MSG="INTERNAL ERROR: $*" DATE=`date` # echo it so that the user can see it or cron can trap it (split line # to avoid confusing 'ident') echo "$PROGNAME: \ $MSG" >&2 # try to write it into $LOG_FILE if that's defined - other message types # treat unwritable but defined LOG_FILE as an error ( echo "$DATE: \ $MSG" >> ${LOG_FILE:-/dev/null} ) 2>/dev/null # try to syslog it - other message types are only syslog'ed if there is # no controlling terminal (as when run from 'at' or 'cron') logger -i -p local0.alert -t $PROGNAME "$MSG" # try to log it on the console echo "$DATE: \ $MSG" > /dev/console exit 2 } error() { DO_EXIT=${ERRORS_CAUSE_EXITS:-true} STDIN=false while :; do case "$1" in -) STDIN=true ;; -e) DO_EXIT=true ;; -ne) DO_EXIT=false ;; -*) internal "invalid option to error() '$1'" ;; *) break ;; esac shift done if [ ${VERBOSE_LEVEL:-2} -ge 1 ]; then DATE=`date` { { [ $STDIN = false ] && echo "$@"; } || cat; } | while read LINE; do MSG="ERROR: $LINE" ( echo "$DATE: \ $MSG" >> ${LOG_FILE:-/dev/null} ) 2>/dev/null || internal "log file $LOG_FILE not writable!" { [ -t 2 ] && echo "$PROGNAME: \ $MSG" >&2; } || logger -i -p $FACILITY.err -t $PROGNAME "$MSG" done fi [ $DO_EXIT = false ] && return 1 exitdel exit 1 } warning() { STDIN=false while :; do case "$1" in -) STDIN=true ;; -*) internal "invalid option to error() '$1'" ;; *) break ;; esac shift done if [ ${VERBOSE_LEVEL:-2} -ge 2 ]; then DATE=`date` { { [ $STDIN = false ] && echo "$@"; } || cat; } | while read LINE; do MSG="WARNING: $LINE" ( echo "$DATE: \ $MSG" >> ${LOG_FILE:-/dev/null} ) 2>/dev/null || internal "log file $LOG_FILE not writable!" { [ -t 2 ] && echo "$PROGNAME: \ $MSG" >&2; } || logger -i -p $FACILITY.warning -t $PROGNAME "$MSG" done fi return 0 } debug() { STDIN=false while :; do case "$1" in -) STDIN=true ;; -*) internal "invalid option to error() '$1'" ;; *) break ;; esac shift done LEVEL=$1 shift if [ ${VERBOSE_LEVEL:-2} -ge $LEVEL ]; then DATE=`date` { { [ $STDIN = false ] && echo "$@"; } || cat; } | while read LINE; do MSG="DEBUG[$LEVEL]: $LINE" ( echo "$DATE: \ $MSG" >> ${LOG_FILE:-/dev/null} ) 2>/dev/null || internal "log file $LOG_FILE not writable!" { [ -t 2 ] && echo "$PROGNAME: \ $MSG" >&2; } || logger -i -p $FACILITY.debug -t $PROGNAME "$MSG" done fi return 0 } info() { STDIN=false while :; do case "$1" in -) STDIN=true ;; -*) internal "invalid option to error() '$1'" ;; *) break ;; esac shift done if [ ${VERBOSE_LEVEL:-2} -ge 3 ]; then DATE=`date` { { [ $STDIN = false ] && echo "$@"; } || cat; } | while read LINE; do MSG="INFO: $LINE" ( echo "$DATE: \ $MSG" >> ${LOG_FILE:-/dev/null} ) 2>/dev/null || internal "log file $LOG_FILE not writable!" { [ -t 2 ] && echo "$PROGNAME: \ $MSG" >&2; } || logger -i -p $FACILITY.info -t $PROGNAME "$MSG" done fi return 0 } ############################################################################### # # GENERIC SUPPORT FUNCTIONS (not specifically related to backup task) # ############################################################################### locatecmd() { for POSSCMD in $*; do [ X`whichcmd $POSSCMD` != X ] && { echo $POSSCMD; return 0; } done return 1 } whichcmd() { for DIR in `echo $PATH | sed 's/:/ /g'`; do [ -x $DIR/$1 ] && { echo $DIR/$1; return 0; } done return 1 } ############################################################################### # # TEMPORARY FILE MANAGEMENT FUNCTIONS # ############################################################################### delonexit() { DELONEXIT="$DELONEXIT $*" } dontdelonexit() { for FILE in $*; do DELONEXIT=`echo $DELONEXIT | sed "s@$FILE@@g"` done } exitdel() { debug 50 "cleaning up" rm -f $DELONEXIT } sighandler() { warning "signal recieved, clearing up and exiting" exitdel exit 3 } ############################################################################## # # LOCK FUNCTIONS # ############################################################################## gen_lock_file_name() { JOB_ID=$1 shift echo $SPOOL_DIR/$JOB_ID/lock } lock() { debug 5 "lock: locking $1" LOCKFILE=`gen_lock_file_name $1` { echo $$ > $LOCKFILE.$$; } 2>/dev/null || error -e "can't create lock" ln $LOCKFILE.$$ $LOCKFILE 2>/dev/null && { rm $LOCKFILE.$$; return 0; } HOLDING_PID=`cat $LOCKFILE` [ "X$HOLDING_PID" = X ] && { rm $LOCKFILE.$$; error -e "empty lockfile: $LOCKFILE"; } [ -d /proc/$HOLDING_PID ] && { rm $LOCKFILE.$$; error -e "program running already! (pid=$HOLDING_PID)"; } info "removing stale lock (pid=$HOLDING_PID)" rm -f $LOCKFILE ln $LOCKFILE.$$ $LOCKFILE 2>/dev/null && { rm $LOCKFILE.$$; return 0; } rm $LOCKFILE.$$ error -e "couldn't lock after removing stale lockfile" } unlock() { debug 5 "unlock: unlocking $1" LOCKFILE=`gen_lock_file_name $1` rm -f $LOCKFILE } ############################################################################### # # ENTRY POINT # ############################################################################### # ensure temporary files are private umask 077 # ensure temporary files are deleted if we get interrupted trap sighandler 1 2 15 # do the main thing! main "$@" # yes, i know, this would be the default, but just for clarity exit $?