#!/bin/sh - # # worldwatch -- Script to watch the progress on "make buildworld". # # Copyright (C) 2007 by Oliver Fromme # All rights reserved. # # Usage: Just type "worldwatch" instead of "make buildworld". # You must be in the /usr/src directory, of course. # Options are passed directly to make(1), so you can # type "worldwatch -j 4", for example. # LOGDIR="/tmp" # Log output from buildworld here. STTDIR="/var/db" # Record statistics data here. ME="${0##*/}" read win_rows win_cols <&2 exit 1 ;; esac max_width=$(( $win_cols - 1 )) if [ $max_width -gt 79 ]; then max_width=79 fi if [ "_${WORLDWATCH_ACTIVE}" = _yes ]; then # # OK, we're running as a shell process inside window(1). # Next we need to find out whether we're running in the # progress window (that's just two lines at the top) or # in the buildworld window. To do that, we simply look # at the number of rows we have. # export SHELL=/bin/sh if [ $win_rows -eq 2 ]; then # # We're in the progress window. Just keep # reading lines from the FIFO and display # them appropriately. # export TC_HOME="$(tput ho)" awk ' BEGIN { prog = stage = "" tc_home = ENVIRON["TC_HOME"] } { if (/^>/) { stage = " " $0 printf "%-'$max_width's%s", prog stage, tc_home fflush() } else if (/^=/) { printf "\n%-'$max_width's%s", $0, tc_home fflush() } else { prog = $0 " " printf "%s%s", prog, tc_home fflush() } } ' "$FIFO" else # # We're in the buildworld window. Run the # actual buildworld command and filter the # output while feeding the FIFO. # # When done (or interrupted), send a SIGHUP # to our parent which is the window(1) process. # my_pid=$$ parent=$(ps -alx | awk '($2=="'$my_pid'"){print $3;exit}') Cancel() { echo "C" > "${FIFO%/*}/exitcode" kill -HUP $parent sleep 0.5 exit 0 } trap Cancel 1 2 3 15 { make $MAKE_ARGS < /dev/null 2>&1 echo "$?" >> "${FIFO%/*}/exitcode" } \ | awk ' function hms (s) { if (s < 3600) return sprintf("%d:%02d", s/60, s%60) _h = int(s/3600) s %= 3600 return sprintf("%d:%02d:%02d", _h, s/60, s%60) } BEGIN { eta = 0 last = 0 perc = 0 fifo = "'"$FIFO"'" data = "'"$DATA"'.new" prev = "'"$DATA"'" full = "'"$FULL"'" start = '"$SYSTIME"' while ((getline < prev) > 0) { oldmax = $1 + 0 tag = $2 " " $3 if (tm[tag]) { for (i = 2; tm[tag i]; i++) ; tag = tag i } tm[tag] = oldmax } close (prev) } { print print > full if (/^(===|>>)> /) { now = '"$SYSTIME"' if (/^=/) { offs = now-start print offs, $2, $3 > data tag = $2 " " $3 if (seen[tag]) { for (i = 2; seen[tag i]; i++) ; tag = tag i } seen[tag] = 1 oldoffs = tm[tag] if (oldoffs > 0) { perc = (oldoffs * 100) / oldmax eta = (oldmax * offs) \ / oldoffs - offs } } if (now != last || /^>/) { last = now print $0 > fifo printf "%s (%.1f%%), %s to go.\n", \ hms(now-start), perc, hms(eta) \ > fifo } } } END { dur = '"$SYSTIME"' - start print "Total duration:", dur, "seconds =", hms(dur) } ' sleep 0.1 trap 1 2 3 15 kill -HUP $parent fi # # We cannot exit right away here, because window(1) # prompts for a command when a process exits. We # do not want that. Therefore we wait in a small # loop. We will either get killed when window(1) # terminates, or -- just to be safe -- we exit when # the FIFO doesn't exist any longer. # # Actually the time window is very small, so the # loop body probably won't be executed more than # once. Therefore this kind of "polling" isn't as # bad as it might look. ;-) # while :; do if [ ! -e "$FIFO" ]; then exit 0 fi sleep 1 done fi # # At this point we're running as the "master" process # which will call window(1) for spawning the above "slaves". # First try to parse the command line. # MAKE_ARGS="$*" export MAKE_ARGS # # Look for the make target. Take the last parameter that # doesn't look like a variable assignment. If not found, # the default is "buildworld". # while [ $# -gt 0 ]; do case "$1" in -[ABeiknPqrSstvX]) ;; -[CDdEfIjmVx]?*) ;; -[CDdEfIjmVx]) shift ;; -*) echo "Cannot parse argument: \"$1\"" >&2 exit 1 ;; *=*) ;; *) WW_TARGET="$i" ;; esac shift done if [ -z "$WW_TARGET" ]; then WW_TARGET="buildworld" MAKE_ARGS="$MAKE_ARGS buildworld" fi # # Now check the terminal size, then create the FIFO. # if [ $win_rows -lt 10 ]; then echo "${ME}: Terminal is too small, need at least 10 rows!" >&2 exit 1 fi # # FreeBSD 4 has gnu awk, FreeBSD 6 has bsd awk. Unfortunately # the latter is crippled and doesn't support systime(). We try # to find out if systime() is supported. If not, we use srand() # instead ... it's a dirty hack, but it works most of the time. # case "$(awk 'BEGIN{print systime()}' 2>/dev/null)" in ""|*[!0-9]*) SYSTIME="srand()" ;; *) SYSTIME="systime()" ;; esac export SYSTIME # # Create a FIFO for communication between the slave processes. # fifo_dir=$(mktemp -d "$LOGDIR/${ME}.tmp.$$.XXXXXX") Cleanup() { rm -rf "$fifo_dir" exit 1 } trap Cleanup 1 2 3 15 FIFO="$fifo_dir/fifo" export FIFO mkfifo "$FIFO" DATA="$STTDIR/${ME}.stat" export DATA FULL="$LOGDIR/${ME}.log" export FULL # # Now we're ready for action. # export WORLDWATCH_ACTIVE=yes SHELL="$0" window -t -f -c 'cursormodes(0);window(0,0,2);window(3,0)' exit_code=$(head -1 "$fifo_dir/exitcode") # # Clean up and exit. # rm -rf "$fifo_dir" if [ "$exit_code" = C ]; then echo "${ME} cancelled." exit_code=1 elif [ "${exit_code:-0}" = 0 ]; then echo "${ME} finished successfully." mv -f "$DATA".new "$DATA" else echo "${ME} terminated with exit code ${exit_code}." fi exit ${exit_code:-0} #--