home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1990 / 12 / bauer.asc next >
Text File  |  1990-11-15  |  36KB  |  973 lines

  1. _CONTROLLING BACKGROUND PROCESSES UNDER UNIX_
  2. by Barr E. Bauer
  3.  
  4. [LISTING ONE]
  5.  
  6. origin=`hostname`
  7.  
  8. # run - the user interface component of the shepard system -- B. E. Bauer 1990
  9. # configuration files associated with run:
  10. #   .run.ini    defaults for script,dataset,host,datadir.
  11. #   .current    jobs originating from workstation environment
  12. #   .hosts      host machines able to run shepard
  13. #   .tasklist   list of tasks. Has flags for shepard
  14. #   .runscripts list of machines and possible scripts
  15. #   these files must be located in the login directory
  16. # flag (and task definitions) definitions, Used in case statement
  17. # and passed as actual flag arguments to shepard:
  18. #    x    executes (submits) a job
  19. #    m    monitors job
  20. #    p    probes job
  21. #    s    status of running jobs on all platforms
  22. #    r    list of running jobs
  23. #    k    kill job (with extreme prejudice)
  24. #    t    terminate job in a controlled manner (script dependent)
  25. #    l    list log on remote machine
  26. #    b    bump a waiting job from the .waiting list
  27. #    d    delete a waiting job
  28. #    f    list finished jobs
  29. #    e    list error log
  30. #    c    change host
  31. #    w    list waiting jobs
  32. #    a    list restart jobs
  33. #    g    restart a restartable job
  34.  
  35. #place the date/time in day-month-year@time single string format
  36. set - `date`
  37. year=$6 month=$2 day=$3 tm=$4
  38. datetime=$day-$month-$year@$tm
  39.  
  40. echo 'welcome to run on '$origin' at '$datetime
  41.  
  42. . $HOME/.run.ini    #source the run-script defaults
  43. . $HOME/.shepard.ini # has network definition
  44.  
  45. # check for finished jobs, update list, display finished list
  46. # find jobs with status RUNNING, check host for status
  47.  
  48. if (test -f $HOME/.current) then
  49.     cnt=`grep -c DONE $HOME/.current`
  50.     if (test "$cnt" != "0" ) then
  51.         awk 'BEGIN {
  52.             printf "\njobs recently finished\n"
  53.             printf "\n%-10s %-10s %-8s %-21s %-21s\n\n",\
  54.                 "script","dataset","host","start","end"
  55.         } $7 == "DONE" {
  56.             printf "%-16s%-16s%-8s%-20s%-20s\n",$1,$2,$3,$5,$6
  57.         } ' $HOME/.current >tmp
  58.         echo ' '; cat tmp   # display list of completed jobs
  59.         echo 'press any key to continue \c'; read sel
  60.         cat tmp >> $HOME/run.log   # completed job data to runlog 
  61.         awk '$7 != "DONE" {
  62.             print $0
  63.         } ' $HOME/.current >tmp
  64.         mv tmp $HOME/.current
  65.     else
  66.         echo "no new finished jobs"
  67.     fi
  68. fi
  69.  
  70. # set default host. All activities focus on that host until changed 
  71. awk 'BEGIN {
  72.     n=1
  73.     printf "\n----- current hosts -------------------------\n\n"
  74. } { 
  75.     if ("'$defhost'" == $1) 
  76.         printf "%-3s%-16s%s %s %s  (default)\n",n,$1,$2,$3,$4
  77.     else printf "%-3s%-16s%s %s %s\n",n,$1,$2,$3,$4
  78.     n++
  79. END {
  80.     printf "\nselect a host machine by number: "
  81. }' $HOME/.hosts 
  82. read sel
  83. if (test -z "$sel") then
  84.     host=$defhost
  85. else
  86.     sel=`awk 'BEGIN {n=1}{if ("'$sel'" == n) print $1; n++}' $HOME/.hosts` 
  87.     host=$sel; defhost=$sel
  88. fi
  89. loop=YES
  90.  
  91. # top of loop. exit with <ret>
  92. while (test "$loop" = "YES")
  93. do    
  94.     # display menu of tasks    
  95.     echo ' '; echo 'current host is ' $host
  96.     echo ' '
  97.     awk ' BEGIN {
  98.         n=1
  99.         printf "\t#      flag          task\n"
  100.         printf "\t----------------------------------------------\n" 
  101.     }{
  102.         printf "\t%-3s\t%s\n",n,$0
  103.         n++
  104.     }
  105.     END {
  106.         printf "\ntask selection number [<ret> to exit]: "
  107.     } ' $HOME/.tasklist
  108.     read sel
  109.     # look up value for shepard flag associated with task. 
  110.     # Use the flag in the case statement 
  111.     task=`awk 'BEGIN {n=1} {if("'$sel'" == n) print $2; n++}' $HOME/.tasklist`
  112.     flag=`awk 'BEGIN {n=1} {if("'$sel'" == n) print $1; n++}' $HOME/.tasklist`
  113.  
  114.     # if response is <ret>, exit while loop
  115.     if (test -z "$sel") then
  116.         break
  117.     fi
  118.  
  119.     case $flag in
  120.     -x) # start a job. Queries for script, dataset, datadir
  121.         # list scripts available only on selected host
  122.         awk ' BEGIN {
  123.             n=1
  124.             def=0
  125.             printf "\n# (host) script"
  126.             printf "\n-------------------------------------\n"
  127.         } 
  128.         "'$host'" == $1 {
  129.             if ($2 == "'$defscript'") {
  130.                 printf "%-2s %s  (default)\n",n,$0
  131.                 def = n
  132.         }
  133.             else printf "%-2s %s\n",n,$0
  134.             n++
  135.         } 
  136.         END {
  137.             printf "\nselect a script by number [%s]: ",def
  138.         } ' $HOME/.runscripts    
  139.         read tmp
  140.         # look up the script selected by number (must be on one line)
  141.         sel=`awk 'BEGIN{n=1} "'$host'"==$1 {if("'$tmp'" == n) print $2; n++} ' $HOME/.runscripts`
  142.         if (test "$sel" = "") then
  143.             script=$defscript
  144.         else
  145.             script=$sel; defscript=$sel
  146.         fi
  147.         echo 'selected script is '$script
  148.         # get the dataset name
  149.         echo ' '; echo 'enter dataset name ['$defdata']: \c'
  150.         read sel
  151.         if (test "$sel" = "") then    # substitute default for <ret>
  152.             dataset=$defdat
  153.         else
  154.             dataset=$sel; defdata=$sel
  155.         fi
  156.         echo 'selected dataset is '$dataset
  157.         # get the directory where the data is located
  158.         # if $SHEPARD_NETWORK is set to "remote", data moves between machines
  159.         # using nfs otherwise, data is retained on server 
  160.         # home directory on the host machine, then back when done
  161.         echo ' '; echo 'enter directory of data on '
  162.         case $SHEPARD_NETWORK in
  163.             remote) echo $iam': \c';;
  164.                nfs) echo $iam' using nfs mount on '$host': \c';;
  165.             server) echo $host': \c'; defdir='$HOME';;
  166.         esac
  167.         read sel
  168.         if (test "$sel" = "") then    # substitute default for <ret>
  169.             datadir=$defdir
  170.         else
  171.             datadir=$sel; defdir=$sel
  172.         fi
  173.         echo 'selected directory is '$datadir
  174.         # append new job entry to $HOME/.current
  175.         llist='$script $dataset $host $datadir $datetime'
  176.         echo $llist 'out' 'STARTED' >>$HOME/.current
  177.         if (test "$origin" = "$host") then
  178.             shepard $flag $script $dataset $host $datadir
  179.         else
  180.             rsh $host shepard $flag $script $dataset $origin $datadir
  181.         fi;;
  182.     -s) # listing of current file. shows activity on other platforms 
  183.         awk ' BEGIN {
  184.             fmt="%-5s %-16s %-16s %-21s %-16s\n"
  185.             dash5="-----"
  186.             dash16="----------------"
  187.             dash21="---------------------"
  188.             n=1
  189.             printf "\n\ncurrent job status\n\n"
  190.             printf fmt,"#","script","dataset","submitted","status"
  191.             printf fmt,dash5,dash16,dash16,dash21,dash16
  192.             printf "\n"
  193.         } {
  194.             printf fmt,n,$1,$2,$5,$7
  195.             n++
  196.         }
  197.         END {
  198.             printf "\npress any key to continue "
  199.         } ' $HOME/.current
  200.         read sel;;
  201.     -[ktpdbg])
  202.         # these are all list processing commands using pick an item menuing
  203.         # the menu is generated by shepard on the selected host
  204.         # the item is picked in run and the selection happens in shepard 
  205.         case $flag in
  206.             -[ktp]) lflag='-r';;  # list running jobs
  207.              -[db]) lflag='-w';;  # list waiting jobs
  208.                 -g) lflag='-a';;  # list restartable jobs
  209.         esac 
  210.         if (test "$origin" = "$host") then
  211.             shepard $lflag dummy2 dummy3 dummy4 dummy5
  212.         else
  213.             rsh $host shepard $lflag dummy2 dummy3 dummy4 dummy5
  214.         fi
  215.         echo ' '; echo 'select number of job to \c'
  216.         case $flag in
  217.             -k) echo 'kill \c';;
  218.             -t) echo 'halt gracefully \c';;
  219.             -g) echo 'restart \c';;
  220.             -d) echo 'remove from waiting queue \c';;
  221.             -b) echo 'bump to top of queue \c';;
  222.             -p) echo 'probe running status \c';;
  223.         esac            
  224.         read sel    # select one from list
  225.         arg5=$sel
  226.         if (test "$origin" = "$host") then
  227.             shepard $flag dummy2 dummy3 dummy4 $arg5
  228.         else
  229.             rsh $host shepard $flag dummy2 dummy3 dummy4 $arg5
  230.         fi;;
  231.     -c) # change hosts
  232.         awk 'BEGIN {
  233.             n=1
  234.             printf "----- current hosts -------------------------\n\n"
  235.         } { 
  236.             if ("'$defhost'" == $1) {
  237.                 printf "%-3s%-16s%s %s %s  (default)\n",n,$1,$2,$3,$4 }
  238.             else printf "%-3s%-16s%s %s %s\n",n,$1,$2,$3,$4
  239.             n++
  240.         }
  241.         END {
  242.             printf "select a new host machine by number: "
  243.         }' $HOME/.hosts 
  244.         read sel
  245.         if (test -z "$sel") then
  246.             host=$defhost
  247.         else
  248.             sel=`awk 'BEGIN {n=1}{if ("'$sel'"==n) print $1; n++}' $HOME.hosts` 
  249.         host=$sel; defhost=$sel
  250.         fi;;
  251.     -[rewfalm]) # process listing commands 
  252.         if (test "$origin" = "$host") then
  253.             shepard $flag dummy2 dummy3 dummy4 dummy5
  254.         else
  255.             rsh $host shepard $flag dummy2 dummy3 dummy4 dummy5
  256.         fi
  257.     read sel;;
  258.     *)  # woops 
  259.         echo $flag 'is not a recognized option, try again'
  260.     esac
  261. done    # bottom of while loop
  262.  
  263. # write current values to run-script default file
  264. # $HOME/.run.ini is sourced on invocation in effect restoring the
  265. # last values used. Handy for checking on a previously
  266. # started job - values properly default to the previous 
  267. echo 'defscript='$defscript >$HOME/.run.ini
  268. echo 'defdata='$defdata >>$HOME/.run.ini
  269. echo 'defhost='$defhost >>$HOME/.run.ini
  270. echo 'defdir='$defdir >>$HOME/.run.ini
  271.  
  272. echo 'end of run'
  273.  
  274. [LISTING TWO]
  275.  
  276. trap 'rm -f $HOME/.sheplock; exit' 1 2 3 15
  277.  
  278. # shepard - task management component od shepard system -- B. E. Bauer 1990
  279. # Shepard is the action component of the system. When invoked, it
  280. # owns all the associated files (see top of shepard_queue for list)
  281. # and updates the current file on the originator, log and err files.
  282. # Shepard can be invoked from local or remote machines; it senses
  283. # local or remote operation and behaves accordingly.
  284. # Shepard handles all tasks except for job queueing (shepard_queue) and
  285. # application-specific job probing (defined in $probe_script as sourced
  286. # in 'script'.script). Shepard is called by terminating jobs for cleanup.
  287. # Shepard can be present in several executing copies called by run (the
  288. # user interface) and by completing jobs waiting for cleanup. To avoid
  289. # collision between shepards, absolute ownership of all associated files 
  290. # is essential, and is accomplished by creating a lock file. All other
  291. # versions of shepard have to wait until the first is done.
  292.  
  293. # wait until lock file established insures complete ownership 
  294. # of all files by only one version of shepard at a time
  295. until lockon .sheplock
  296. do sleep 5; done
  297.  
  298. iam=`hostname`
  299. . $HOME/.shepard.ini    # source the initialization file
  300. # do not display greeting message if called from terminating process
  301. if (test "$1" != "-z") then 
  302.     echo 'shepard on '$iam' at '`date`
  303. fi
  304. # if you see the message, you made it. 
  305. # Important verification that remote shell command is functioning
  306.  
  307. # lookup values from files depending on mode
  308. pass=NO
  309. case $1 in    # select the file name associated with flag
  310.    -[ktp])    fname=$HOME/.running; pass=YES;;
  311.     -[bd])    fname=$HOME/.waiting; pass=YES;;
  312.        -g)    fname=$HOME/.restart; pass=YES;;
  313. esac
  314. if (test "$pass" = "YES") then # do the lookup
  315.      scr=`awk 'BEGIN {n=1} {if ("'$5'" == n) print $1; n++}' $fname`
  316.     dset=`awk 'BEGIN {n=1} {if ("'$5'" == n) print $2; n++}' $fname`
  317.     host=`awk 'BEGIN {n=1} {if ("'$5'" == n) print $3; n++}' $fname`
  318.     ddir=`awk 'BEGIN {n=1} {if ("'$5'" == n) print $4; n++}' $fname`
  319.      sel=`awk 'BEGIN {n=1} {if ("'$5'" == n) print $5; n++}' $fname`
  320.    tname=$host':'$scr'('$dset')'    # compact file name
  321. fi
  322.  
  323. # no loop in shepard. Does the command then exits
  324. case $1 in
  325.     -x) # runs job through queue manager which handles submission
  326.         shepard_queue $2 $3 $4 $5;; 
  327.     -m) # system-dependent code here. "big" is using Berkeley UNIX
  328.         # while all others use SYSTEM V. Options to ps are different
  329.         if (test "`hostname`" = "big") then    
  330.             ps -ax | grep -n shepard_exec  # CONVEX specific (for example)
  331.         else
  332.             ps -ef      # SGI IRIS specific (for example)
  333.         fi;;
  334.     -p) # probe job - script-dependent
  335.         # source the file containing application-specific scripts
  336.         . $SHEPARD_DIR/$2.script 
  337.         . $probe_script;;    # defined in sourced file $script.script
  338.         echo 'press <ret> to continue \c'
  339.     -r) # list running jobs on host
  340.         cnt=`wc -l $HOME/.running | awk '{print $1}'`
  341.         if (test "$cnt" = "0") then
  342.             echo ' '; echo 'no jobs running'; echo ' '
  343.         else
  344.             awk ' BEGIN {
  345.                 fmt="\n%-5s %-16s %-16s %-8s\n"
  346.                 printf "\n----- running jobs on %s -----\n","'$host'"
  347.                 printf fmt,"#","script","dataset","pid"
  348.                 printf "----- ---------------- ---------------- --------\n"
  349.                 n=1
  350.             } {
  351.                 printf fmt,n,$1,$2,$5
  352.                 n++
  353.             }
  354.             END {
  355.                 printf "\npress any key to continue "
  356.             } ' $HOME/.running    
  357.         fi;;
  358.     -w) # list waiting jobs on host
  359.         cnt=`wc -l $HOME/.waiting | awk '{print $1}'`
  360.         if (test "$cnt" = "0") then
  361.             echo ' '; echo 'no jobs waiting'; echo ' '
  362.         else
  363.             awk ' BEGIN {
  364.                 fmt="\n%-5s %-16s %-16s %-8s\n"
  365.                 printf "\n----- waiting jobs on %s -----\n","'$host'"
  366.                 printf fmt,"#","script","dataset","position"
  367.                 printf "----- ---------------- ---------------- --------\n"
  368.                 n=1
  369.             } {
  370.                 printf fmt,n,$1,$2,$5
  371.                 n++
  372.             }
  373.             END {
  374.                 printf "\npress any key to continue "
  375.             } ' $HOME/.waiting    
  376.         fi;;
  377.     -a) # list restartable jobs on host
  378.         cnt=`wc -l $HOME/.restart | awk '{print $1}'`
  379.         if (test "$cnt" = "0") then
  380.             echo ' '; echo 'no jobs in restart'; echo ' '
  381.         else
  382.             awk ' BEGIN {
  383.                 fmt="\n%-5s %-16s %-16s %-8s\n"
  384.                 printf "\n----- restartable jobs on %s -----\n","'$host'"
  385.                 printf fmt,"#","script","dataset","position"
  386.                 printf "----- ---------------- ---------------- --------\n"
  387.                 n=1
  388.             } {
  389.                 printf fmt,n,$1,$2,$5
  390.                 n++
  391.             } ' $HOME/.restart 
  392.         fi;;    
  393.     -g) # restart a job from $HOME/.restart and update
  394.         # file to select passed as shell argument 5
  395.         # copys the selected entry to $HOME/.waiting with priority=RESTART
  396.         awk ' BEGIN {
  397.             n=1
  398.         } {                 
  399.             if (n == "'$5'") printf "%s %s %s %s RESTART\n",$1,$2,$3,$4
  400.             n++
  401.         } ' $HOME/.restart >> $HOME/.waiting
  402.         awk ' BEGIN {   # restarted job is purged from $HOME/.restart
  403.              n=1
  404.         } {
  405.             if (n != "'$5'") print $0
  406.             n++
  407.         }' $HOME/.restart > tmp
  408.         mv tmp $HOME/.restart
  409.         echo 'restarting '$tname' at '$datetime >>shepard.log
  410.         #update .current on origin machine
  411.         if (test "$host" = "$iam") then
  412.             run_update -g $scr $dset $sel
  413.         else
  414.             rsh $host run_update -g $scr $dset $sel
  415.         fi
  416.         shepard_queue -r;;   # do the restart 
  417.     -k) # kill job with extreme prejudice
  418.         # pid passed as shell argument 5, assigned to sel
  419.         # running processes have 2 entries in the process list
  420.         # first = shepard_exec and has the pid stored in running
  421.         # second = the executable application
  422.         # searching the process list for first finds second; both
  423.         # must be killed to stop the application: killing shepard_exec
  424.         # alone leaves the application program still running
  425.     if (test "$iam" = "big") then
  426.         cleanup=`ps -axl | awk ' "'$sel'" == $4 {print $3}'`
  427.         else
  428.         cleanup=`ps -ef | awk ' "'$sel'" == $4 {print $3}'`
  429.         fi
  430.         kill -9 $sel
  431.     kill -9 $cleanup
  432.         if (test "$?" = "0") then
  433.             echo 'killed '$tname' at '$datetime >>$HOME/shepard.log
  434.         else
  435.             echo 'status of kill command nonzero - check log for problems'
  436.         fi
  437.     awk ' $5 != "'$sel'" { print $0 }' $HOME/.running > $HOME/tmp
  438.     mv $HOME/tmp $HOME/.running
  439.         #update .current on origin machine
  440.         if (test "$host" = "$iam") then
  441.             run_update -k $scr $dset $sel
  442.         else
  443.             rsh $host run_update -k $scr $dset $sel
  444.         fi
  445.         shepard_queue -q;; # check for waiting jobs
  446.     -t) # terminate job gracefully pass script and origin variables 
  447.         # source the file containing application-specific scripts
  448.         . $SHEPARD_DIR/$2.script 
  449.         . $terminate_script # found in scriptname.script
  450.         echo 'terminated '$tname' at '$datetime >> $HOME/shepard.log
  451.         #update .current on origin machine
  452.         if (test "$host" = "$iam") then
  453.             run_update -t $scr $dset $sel
  454.         else
  455.             rsh $host run_update -t $scr $dset $sel
  456.         fi;;  # when the application exits, it will check for waiting jobs
  457.     -l) # list the job log on host
  458.         tail -30 shepard.log;;   # only the last is generally interesting
  459.     -b) # bump priority of specific job
  460.         # $HOME/.waiting can be in any order, use 2-pass approach
  461.         # pass 1: set desired to zero, increment all others
  462.         # pass 2: change 0 to 1, zero now being easy to spot
  463.         awk ' {
  464.             if ($1 == "'$scr'") {
  465.                 if ($5=="'$sel'") $5 = 0
  466.                 if ($5 < "'$sel'") $5 += 1  
  467.             }
  468.             printf "%s %s %s %s %s\n",$1,$2,$3,$4,$5
  469.         } ' $HOME/.waiting | awk ' {
  470.                 if ($5 == 0) $5 = 1
  471.                 printf "%s %s %s %s %s\n",$1,$2,$3,$4,$5 
  472.         } ' > $HOME/tmp
  473.         mv $HOME/tmp $HOME/.waiting
  474.         echo 'bumped '$tname' at '$datetime >> $HOME/shepard.log
  475.         if (test "$host" = "$iam") then
  476.             run_update -b $scr $dset $sel
  477.         else
  478.             rsh $host run_update -b $scr $dset $sel
  479.         fi;;
  480.     -d) # delete a waiting job from waiting, selected passed as shell arg 5
  481.         # same script/higher priority have their priorities--
  482.         awk ' {
  483.             if ($1 == "'$scr'") {
  484.                 if ($5=="'$sel'") next   # excise deleted job
  485.                 if ($5 > "'$sel'") $5 = $5 - 1  
  486.             }
  487.             print $0
  488.         } ' $HOME/.waiting > tmp
  489.         mv tmp $HOME/.waiting
  490.         echo 'deleted '$tname' at '$datetime >> $HOME/shepard.log
  491.         #update .current on origin machine
  492.         if (test "$host" = "$iam") then
  493.             run_update -d $scr $dset $sel
  494.         else
  495.             rsh $host run_update -d $scr $dset $sel
  496.         fi;;
  497.     -f) # list finished jobs
  498.         awk ' BEGIN {
  499.             fmt="\n%-16s %-16s %-12s\n"
  500.             printf "\n----- finished jobs on %s -----\n","'$host'"
  501.             printf fmt,"script","dataset","origin"
  502.             printf "---------------- ---------------- ------------\n"
  503.         } {
  504.             printf fmt,$1,$2,$3
  505.         }
  506.         END {
  507.             printf "\npress any key to continue "
  508.         } ' $HOME/.finished;;
  509.     -e) # list error log
  510.         tail -30 $HOME/shepard.err;;
  511.     -z) # go to cleanup routine, $5 has the completed jobs pid number
  512.         echo 'finished '$4':'$2'('$3') at '`date` >>shepard.log
  513.         # write entry to .finished
  514.         # run on origin will look here for completed jobs
  515.         echo $2 $3 $4 $5 `date` >> $HOME/.finished
  516.         # excise finished job from $HOME/.running list
  517.         awk '{ 
  518.             if ("'$5'" != $5) print $0 
  519.             }' $HOME/.running >tmp
  520.         mv tmp $HOME/.running
  521.         #update .current on origin machine
  522.         if (test "$4" = "$iam") then
  523.             run_update -f $2 $3 $5
  524.         else
  525.             rsh $4 run_update -f $2 $3 $5
  526.         fi
  527.         #check queue for waiting process
  528.         shepard_queue -q;;
  529. esac
  530.  
  531. rm -f $HOME/.sheplock     # remove locking file 
  532.  
  533. # normal return to run if invoked by remote shell, otherwise terminates
  534.  
  535.  
  536. [LISTING THREE]
  537.  
  538. trap 'rm -f $HOME/tmp; exit' 1 2 3 15
  539.  
  540. # shepard_queue - queue manager for shepard system -- B. E. Bauer 1990
  541. # shepard_queue places jobs in a waiting queue and allows a job
  542. # to actually start if the count of similar jobs running is
  543. # below a user defined threshold. Its like a FIFO queue with a twist.
  544. # This is intended to balance throughput vs system demands on
  545. # multiprocessor high performance computers. Alter for your environment
  546. # jobs in $HOME/.waiting have a number associated with their place in the
  547. # queue. 1=next to start up to limit defined in .limits
  548. # passed arguments:
  549. #   normal queue submit:    1:  script name
  550. #                           2:  dataset name
  551. #                           3:  originating machine name
  552. #                           4:  dataset directory
  553. #   restart                 1:  -r  (no other values passed)
  554. #   queue check             1:  -q  (no other values passed)
  555. #
  556. #   for restart, $HOME/.waiting has the restart job preappended
  557. iam=`hostname`
  558. . $HOME/.shepard.ini    # source the initialization file
  559. mode=NORMAL
  560. if (test "$1" = "-r") then    # restart entry submitted
  561.     # get the script which has the RESTART code (normally passed as $1)
  562.     scr=`awk 'BEGIN {n=0} $5=="RESTART" {print $1}' $HOME/.waiting`
  563.     # find and replace RESTART with last queue slot for corresponding script
  564.     awk ' BEGIN { 
  565.         count = 1 
  566.     } {    
  567.         if ("'$scr'" != $1) print $0
  568.         else if ($5 != "RESTART") {
  569.             count++
  570.             printf "%s %s %s %s %s\n",$1,$2,$3,$4,$5
  571.         }
  572.         else printf "%s %s %s %s %s\n",$1,$2,$3,$4,count
  573.     } ' $HOME/.waiting  > $HOME/tmp
  574.     mv $HOME/tmp $HOME/.waiting
  575. elif (test "$1" != "-q") then   # new job to submit
  576.     # append new job entry to $HOME/.waiting list
  577.     echo $1 $2 $3 $4 'NEW' >> $HOME/.waiting
  578.     # change NEW label to count of jobs having that script
  579.     # newest entry has the highest number/last to be executed
  580.     awk ' BEGIN { 
  581.         count = 1 
  582.     } {    
  583.         if ("'$1'" != $1) print $0
  584.         else if ($5 != "NEW") {
  585.             count++
  586.             print $0
  587.         }
  588.         else printf "%s %s %s %s %s\n",$1,$2,$3,$4,count
  589.     } ' $HOME/.waiting  > $HOME/tmp
  590.     mv $HOME/tmp $HOME/.waiting
  591.     cnt=`awk 'BEGIN{n=0}"'$1'" == $1 {n++} END {print n}' $HOME/.waiting`
  592.     if (test "$3" = "$iam") then
  593.         run_update -w $1 $2 cnt 
  594.     else
  595.         rsh $3 run_update -w $1 $2 cnt
  596.     fi
  597. else
  598.     mode=QUEUE    # flag suppresses terminal response when in -q mode
  599. fi
  600.  
  601. didit=NO  # flag reports job starting status
  602.  
  603. # loop through scripts available on this host
  604. # available scripts are in the environment variable SHEPARD_SCRIPTS
  605.  
  606. # The FIFO queue has a twist: differing job types are subqueued with
  607. # limits for each found in .limits without maintaining separate queue
  608. # structures. This method is easier to implement and permits a maximum
  609. # load balance consisting of a mix of program types, tailored to ones
  610. # needs. In this way, a number of program type 'a' exceeding the limit
  611. # only runs the number set in .limits, while the others queue leaving
  612. # processor time for program types 'b' and 'c'. The optimum load balance is
  613. # determined by the system resource requirements of each program and
  614. # ones needs for throughput; adjusting .limits allows changes on the fly. 
  615.  
  616. for i in $SHEPARD_SCRIPTS
  617. do
  618.     # count jobs actually running for each script, get associated job limit
  619.     if (test -f "$HOME/.running") then
  620.         rcnt=`awk 'BEGIN{n=0} "'$i'"==$1 {n++} END{print n}' $HOME/.running`
  621.     else
  622.         rcnt=0  # set rcnt to 0 if $HOME/.running is not present
  623.     fi
  624.     rlim=`awk '"'$iam'" == $1 && "'$i'" == $2 {print $3}' $HOME/.limits`
  625.     if (test -z "$rlim") then
  626.         rlim=1  # if no limit in $HOME/.limits, one job permitted 
  627.     fi
  628.     # if more running jobs exceeds the limit, continue to next script
  629.     if (test "$rcnt" -ge "$rlim") then
  630.         continue
  631.     fi
  632.     # loop to next script if no jobs waiting with priority=1
  633.     script=`awk ' "'$i'" == $1 && $5 == "1" { print $1}' $HOME/.waiting`
  634.     if (test "$script" != "$i") then
  635.         continue
  636.     fi
  637.     # found one for current script, get the remaining values
  638.     dataset=`awk ' "'$i'" == $1 && $5 == "1" { print $2}' $HOME/.waiting`
  639.     origin=`awk ' "'$i'" == $1 && $5 == "1" { print $3}' $HOME/.waiting`
  640.     datadir=`awk ' "'$i'" == $1 && $5 == "1" { print $4}' $HOME/.waiting`
  641.  
  642.     # put date/time in a single string format
  643.     set - `date`
  644.     day=$3 month=$2 year=$6 tm=$4
  645.     datetime=$day-$month-$year@$tm
  646.     # submit shepard_exec to the background, get its pid
  647.     wait 10    # shepard_queue does not wait for shepard_exec
  648.     nohup shepard_exec $script $dataset $origin $datadir >shepard_junk.log &
  649.     pid=$!  # process identification number - unique for job
  650.     errflag=$?
  651.  
  652.     # shepard_exec did not initiate, for some reason
  653.     # append the shepard_junk.log to shepard.err, alert the user
  654.     # the job is placed in .restart
  655.     if (test "$errflag" != "0") then
  656.         #notify the user
  657.         echo $script'('$dataset') did not start at '$datetime 
  658.         echo '       return code '$errflag
  659.         echo '------ process error logfile contents -----' 
  660.         cat $HOME/shepard_junk.log 
  661.         echo '------ end of log from '$script'('$dataset') -----'
  662.         echo ' '; echo 'check the contents of shepard.err  for details'       
  663.         # update shepard.err
  664.         echo $script'('$dataset') did not start at '$datetime >tmp
  665.         echo '------ process error logfile contents -----' >>tmp
  666.         cat shepard_junk.log >>tmp
  667.         echo '------ end of log from '$script'('$dataset') -----' >>tmp   
  668.         cat tmp >> $HOME/shepard.log; rm tmp
  669.         # remove from $HOME/.waiting, place in .restart
  670.         awk ' $1 == "'$script'" && $2 == "'$dataset'" && $5 == 1 {
  671.             print $0
  672.         }' $HOME/.waiting >> $HOME/.restart
  673.         awk '{
  674.             if ($1 == "'$i'" && $5 == 1) continue
  675.             if ($1 == "'$i'") {
  676.                 $5 = $5 - 1
  677.                 printf "%s %s %s %s %s\n",$1,$2,$3,$4,$5
  678.             }
  679.         }'    $HOME/.waiting > $HOME/tmp
  680.         mv $HOME/tmp $HOME/.waiting
  681.         exit
  682.     fi
  683.  
  684.     didit=YES
  685.     # append job specifics to $HOME/.running
  686.     echo $script $dataset $origin $datadir $pid >>$HOME/.running
  687.     
  688.     # append job info to shepard.log
  689.     echo $script'('$dataset') started '$datetime >>$HOME/shepard.log
  690.     
  691.     # remove running job from $HOME/.waiting, update priority
  692.     awk '{
  693.         if ($1 == "'$i'" && $5 == 1) next
  694.         if ($1 == "'$i'") {
  695.             $5 = $5 - 1
  696.             printf "%s %s %s %s %s\n",$1,$2,$3,$4,$5
  697.         }
  698.     }'    $HOME/.waiting > $HOME/tmp
  699.     mv $HOME/tmp $HOME/.waiting
  700.     #update .current on origin machine
  701.     if (test "$iam" = "$origin") then  
  702.         run_update -r $script $dataset $pid
  703.     else
  704.         rsh $origin run_update -r $script $dataset $pid
  705.     fi
  706.     # if job is successfully started, notify user
  707.     if (test "$didit" = "YES") then
  708.         echo ' '
  709.         echo $script'('$dataset') started on '$iam' at '$datetime
  710.     fi
  711. done
  712.  
  713. if (test "$didit" = "NO") then
  714.     if (test "$mode" != "QUEUE") then
  715.         echo ' '; echo 'no jobs were submitted'
  716.     fi
  717. fi
  718. trap '' 1 2 3 15
  719.  
  720. [LISTING FOUR]
  721.  
  722. trap 'shepard -z $1 $2 $3 $$' 1 2 3 15
  723.  
  724. # shepard_exec - execution potion of shepard system -- B. E. Bauer, 1990
  725. # passed args: 1: script, 2: dataset, 3: origin, 4: datadir
  726.  
  727. . $HOME/.shepard.ini      # source initialization file
  728. . $SHEPARD_DIR/$1.script  # source application-specific definitions
  729.  
  730. # routine to move the required files into the execution environment
  731. # sourcing vs separate shell obviates need to pass values
  732. . $SHEPARD_DIR/$getdata_script  
  733.  
  734. # run the program. Assumes here that stdin, stdout, and stderr are
  735. # required (generally true for UNIX) during execution. All other data
  736. # files were moved into the execution environment by $getdata_script
  737. $exe < $2$inp 1> $2$log 2> $2$err 
  738.  
  739. # source the script to return data back in its proper location
  740. . $SHEPARD_DIR/$putdata_script
  741.  
  742. # clean up and update status files
  743. shepard -z $1 $2 $3 $$    # pid of completing process returns as arg5
  744.  
  745. trap '' 1 2 3 15
  746.  
  747. [LISTING FIVE]
  748.  
  749. trap 'rm -f $HOME/tmp; exit' 1 2 3 15
  750.  
  751. #   run_update - update component of shepard system -- B.E. Bauer 1990
  752. #   updates the .current file to reflect system activities
  753.  
  754. flag=$1 script=$2 dataset=$3 opt=$4
  755. set - `date`
  756. day=$3 month=$2 year=$6 tm=$4
  757. datetime=$day-$month-$year@$tm
  758.  
  759. case $flag in
  760.     -w) stat='WAITING';;
  761.     -r) stat='RUNNING';;
  762.     -g) stat='RESTART';;
  763.     -b) stat='BUMPED';;
  764.     -k) stat='KILLED';;
  765.     -f) stat='DONE';;
  766.     -d) stat='DELETED';;
  767.     -t) stat='TERMINATED';;
  768. esac
  769.  
  770. awk ' {
  771.     if ("'$script'" == $1 && "'$dataset'" == $2) {
  772.         printf "%s %s %s %s %s %s %s\n",$1,$2,$3,$4,$5,"'$datetime'","'$stat'"
  773.     }
  774.     else print $0
  775. }' $HOME/.current >$HOME/tmp
  776. mv $HOME/tmp $HOME/.current
  777.  
  778. trap '' 1 2 3 15
  779.  
  780.  
  781. [LISTING SIX]
  782.  
  783. big bmin31lv     Batchmin large  (<2000 atoms)
  784. big bmin31mv     Batchmin medium (<1000 atoms)
  785. big spartan      ab initio electronic structure calculation 
  786. big amber        biological structure simulation
  787. big smapps       Monte Carlo peptide simulation
  788. big ampac        semi-empirical electronic structure calculation
  789. big dspace       NMR distance -> structure 
  790. moe bmin31ls     Batchmin large  (<2000 atoms) use with caution!
  791. moe bmin31ms     Batchmin medium (<1000 atoms) default
  792. moe bmin31ss     Batchmin small  (<250 atoms)
  793. moe amber        biological structure simulation
  794. moe ampac        semi-empirical electronic structure calculation
  795. moe spartan      ab initio electronic structure calculation
  796. moe smapps       Monte Carlo peptide simulation
  797. larry bmin31ss   Batchmin small (<250 atoms)
  798. larry ampac      semi-empirical electronic structure calculation
  799.  
  800.  
  801. [LISTING SEVEN]
  802.  
  803. larry SGI IRIS 4D/25TG (B-1-3-09)
  804. curly SGI IRIS 4D/120GTX (B-8-3-22; CADD room)
  805. moe SGI IRIS 4D/240s (B-8-3-22; CADD room)
  806. big CONVEX 220 (B-20-B)
  807.  
  808. [LISTING EIGHT]
  809.  
  810. -x execute (submit) a job
  811. -m monitor remote host process status (ps command)
  812. -p probe running job
  813. -s status of running jobs on all platforms
  814. -r running job list
  815. -k kill job (with extreme prejudice)
  816. -t terminate job gracefully
  817. -g restart job
  818. -l log file on remote machine (tail -30)
  819. -b bump waiting job to next
  820. -d delete a waiting job
  821. -f finished job list on host machine
  822. -e error log on host machine (tail -30)
  823. -c change hosts 
  824. -w waiting job list on host machine
  825. -a restartable job list on host machine
  826.  
  827. [LISTING NINE]
  828.  
  829. # Loads values for script, dataset, host, and datadir last used by run.
  830. # This file is recreated at the end of run.
  831.  
  832. defscript=bmin31lv   
  833. defdata=bmintest3      
  834. defhost=big           
  835. defdir=pla2
  836.                 
  837.  
  838. [LISTING TEN]
  839.  
  840. big bmin31lv 3
  841. big bmin31mv 1
  842. big spartan 1
  843. big amber 2
  844. big smapps 1
  845. big ampac 3
  846. moe bmin31ms 2
  847. moe bmin31ss 4
  848. moe amber 2
  849. moe smapps 1
  850. moe ampac 4
  851. moe spartan 1
  852. larry bmin31ss 1
  853. larry ampac 1
  854.  
  855. [LISTING ELEVEN]
  856.  
  857. # Definitions for runnable scripts, dataset network movement and directory for
  858. # various files. The runnable scripts must be in agreement with contents of 
  859. # .runscripts. Behavior of network for the originating machine is set here.
  860.  
  861. # options for SHEPARD_NETWORK: server, nfs, remote
  862. # SHEPARD_DIR is location of application-specific shepard scripts
  863.                 
  864. # this file is sourced and executes directly in environment of script
  865.                 
  866. case `hostname` in
  867.     larry|curly) # SGI IRIS workstation definitions
  868.         SHEPARD_SCRIPTS='bmin31ss ampac'
  869.         SHEPARD_NETWORK=server
  870.         SHEPARD_DIR=$HOME/shepard_dir;;
  871.     moe) # SGI IRIS-240 compute server definitions
  872.         SHEPARD_SCRIPTS='bmin31ss bmin31ms amber smapps ampac spartan'
  873.         SHEPARD_NETWORK=server
  874.         SHEPARD_DIR=$HOME/shepard_dir;;
  875.     big) # CONVEX specific definitions
  876.         SHEPARD_SCRIPTS='bmin31lv bmin31mv amber smapps ampac spartan dspace'
  877.         SHEPARD_NETWORK=server
  878.         SHEPARD_DIR=$HOME/shepard_dir;;
  879. esac
  880.  
  881. [LISTING TWELVE]                
  882.  
  883. # bmin31lv script for large vector (CONVEX) version of Batchmin v 3.1
  884. exe=bmin31lv                         # the executable (in PATH)
  885. inp=.com                             # extension for standard input
  886. log=.log                             # extension for standard output
  887. err=.err                             # extension for error output (channel 2)
  888. getdata_script=bmin31lv.getdata      # get the input datafiles
  889. putdata_script=bmin31lv.putdata      # put the output back 
  890. terminate_script=bmin31lv.terminate  # application-specific shutdown
  891. probe_script=bmin31lv.probe          # conducts an application-specific probe
  892.  
  893. [LISTING THIRTEEN]
  894.  
  895. # bmin31lv.getdata: get data script. Sourced in shepard_exec
  896. # shell args: 2: dataset, 3:origin, 4: datadir
  897. # datadir is dependent on network choice:
  898. #       server: host-$HOME/datadir (host-$HOME is prepended)
  899. #          nfs: nfs path of data from host to origin machines
  900. #       remote: origin-$HOME/datadir (origin-$HOME is prepended)
  901.  
  902. case $SHEPARD_NETWORK in
  903.     server) cd $4;;     # data stays put on host machine
  904.     remote) rsh $3 cat $4/$2.dat >$2.dat   # move data. This is a kluge
  905.             rsh $3 cat $4/$2$inp >$2$inp;; # remote cat puts output on host
  906.        nfs) cp $4/$2.dat .     # copy via remotely mounted nfs dir
  907.             cp $4/$2$inp . ;;
  908. esac
  909.  
  910. [LISTING FOURTEEN]
  911.  
  912.  
  913. # bmin31lv.putdata: put data script. Sourced in shepard_exec
  914. # shell args: 2: dataset, 3:origin, 4: datadir
  915. # datadir is dependent on network choice:
  916. #       server: host-$HOME/datadir (host-$HOME is prepended)
  917. #          nfs: nfs path of data from host to origin machines
  918. #       remote: origin-$HOME/datadir (origin-$HOME is prepended)
  919.  
  920. # all application-specific output files are moved, if necessary
  921. case $SHEPARD_NETWORK in
  922.     server) cd $HOME;;   # movement of files is not necessary 
  923.     remote) rsh $3 cat ">"$4/$2.out <$2.out     # another network kluge
  924.             rsh $3 cat ">"$4/$2$log <$2$log     # remote cat with ">" writes
  925.             rsh $3 cat ">"$4/$2$err <$2$err;;   # to remote. < read local.
  926.        nfs) cp $2.out $4/$2.out
  927.             cp $2$log $4/$2$log
  928.             cp $2$err $4/$2$err;;
  929. esac
  930.  
  931. [LISTING FIFTEEN]
  932.  
  933. # sourced from shepard
  934. # batchmin terminates when it finds dataset.stp in execution dir
  935.  
  936. case $SHEPARD_NETWORK in
  937.         server) echo 'help me, please help me' > $4/$2.stp;;
  938.     remote|nfs) echo 'help me, please help me' > $2.stp;;
  939. esac
  940.  
  941. [LISTING SIXTEEN]
  942.  
  943. # Sourced from shepard. $dset=jobname, $ddir=directory, $log=logfile ext 
  944. # Script prints the last 30 lines of the log file from the selected job
  945.  
  946. case $SHEPARD_NETWORK in
  947.         server) tail -30 $ddir/$dset$log;;
  948.     remote|nfs) tail -30 $dset$log;;
  949. esac
  950.  
  951. [LISTING SEVENTEEN]
  952.  
  953. /* lockon.c - creates lock file from argv[1] having no privelege 
  954.    B. E. Bauer, 1990
  955.  */
  956. main (argc, argv)
  957. int argc;
  958. char *argv[];
  959. {
  960.     int fp, locked;
  961.     
  962.     locked = 0;
  963.     if (argc != 1) {
  964.         printf ("\nuseage: lockon lockfile\n");
  965.         exit (0);
  966.     }
  967.     if ((fp = creat(argv[1], 0)) < 0) ++locked;
  968.     else close(fp);
  969.     return (locked);
  970. }
  971.  
  972.