hgforest.sh revision 2281:4353c659dfde
1210284Sjmallett#!/bin/sh 2232812Sjmallett# 3215990Sjmallett# Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. 4210284Sjmallett# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5210284Sjmallett# 6215990Sjmallett# This code is free software; you can redistribute it and/or modify it 7215990Sjmallett# under the terms of the GNU General Public License version 2 only, as 8215990Sjmallett# published by the Free Software Foundation. 9210284Sjmallett# 10215990Sjmallett# This code is distributed in the hope that it will be useful, but WITHOUT 11215990Sjmallett# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12210284Sjmallett# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13215990Sjmallett# version 2 for more details (a copy is included in the LICENSE file that 14215990Sjmallett# accompanied this code). 15215990Sjmallett# 16215990Sjmallett# You should have received a copy of the GNU General Public License version 17215990Sjmallett# 2 along with this work; if not, write to the Free Software Foundation, 18232812Sjmallett# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19215990Sjmallett# 20215990Sjmallett# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21215990Sjmallett# or visit www.oracle.com if you need additional information or have any 22215990Sjmallett# questions. 23215990Sjmallett# 24215990Sjmallett 25215990Sjmallett# Shell script for a fast parallel forest/trees command 26215990Sjmallett 27215990Sjmallettusage() { 28215990Sjmallett echo "usage: $0 [-h|--help] [-q|--quiet] [-v|--verbose] [-s|--sequential] [--] <command> [commands...]" > ${status_output} 29232812Sjmallett echo "command format : mercurial-command [ "jdk" ] [ extra-url ]" 30215990Sjmallett echo "command option: jdk : used only with clone command to request just the extra repos for JDK-only builds" 31215990Sjmallett echo "command option : extra-url : server hosting the extra repositories" 32215990Sjmallett echo "Environment variables which modify behaviour:" 33215990Sjmallett echo " HGFOREST_QUIET : (boolean) If 'true' then standard output is redirected to /dev/null" 34215990Sjmallett echo " HGFOREST_VERBOSE : (boolean) If 'true' then Mercurial asked to produce verbose output" 35215990Sjmallett echo " HGFOREST_SEQUENTIAL : (boolean) If 'true' then repos are processed sequentially. Disables concurrency" 36215990Sjmallett echo " HGFOREST_GLOBALOPTS : (string, must begin with space) Additional Mercurial global options" 37215990Sjmallett echo " HGFOREST_REDIRECT : (file path) Redirect standard output to specified file" 38210284Sjmallett echo " HGFOREST_FIFOS : (boolean) Default behaviour for FIFO detection. Does not override FIFOs disabled" 39210284Sjmallett echo " HGFOREST_CONCURRENCY: (positive integer) Number of repos to process concurrently" 40210284Sjmallett echo " HGFOREST_DEBUG : (boolean) If 'true' then temp files are retained" 41210284Sjmallett exit 1 42210284Sjmallett} 43210284Sjmallett 44210284Sjmallettglobal_opts="${HGFOREST_GLOBALOPTS:-}" 45215990Sjmallettstatus_output="${HGFOREST_REDIRECT:-/dev/stdout}" 46210284Sjmallettqflag="${HGFOREST_QUIET:-false}" 47210284Sjmallettvflag="${HGFOREST_VERBOSE:-false}" 48210284Sjmallettsflag="${HGFOREST_SEQUENTIAL:-false}" 49210284Sjmallettwhile [ $# -gt 0 ] 50210284Sjmallettdo 51232812Sjmallett case $1 in 52210284Sjmallett -h | --help ) 53215990Sjmallett usage 54215990Sjmallett ;; 55215990Sjmallett 56215990Sjmallett -q | --quiet ) 57215990Sjmallett qflag="true" 58215990Sjmallett ;; 59215990Sjmallett 60232812Sjmallett -v | --verbose ) 61215990Sjmallett vflag="true" 62215990Sjmallett ;; 63215990Sjmallett 64215990Sjmallett -s | --sequential ) 65215990Sjmallett sflag="true" 66215990Sjmallett ;; 67232812Sjmallett 68215990Sjmallett '--' ) # no more options 69215990Sjmallett shift; break 70215990Sjmallett ;; 71215990Sjmallett 72215990Sjmallett -*) # bad option 73215990Sjmallett usage 74215990Sjmallett ;; 75215990Sjmallett 76215990Sjmallett * ) # non option 77215990Sjmallett break 78215990Sjmallett ;; 79232812Sjmallett esac 80215990Sjmallett shift 81215990Sjmallettdone 82215990Sjmallett 83215990Sjmallett# debug mode 84210284Sjmallettif [ "${HGFOREST_DEBUG:-false}" = "true" ] ; then 85215990Sjmallett global_opts="${global_opts} --debug" 86210284Sjmallettfi 87215990Sjmallett 88215990Sjmallett# silence standard output? 89215990Sjmallettif [ ${qflag} = "true" ] ; then 90232816Sjmallett global_opts="${global_opts} -q" 91215990Sjmallett status_output="/dev/null" 92215990Sjmallettfi 93215990Sjmallett 94215990Sjmallett# verbose output? 95210284Sjmallettif [ ${vflag} = "true" ] ; then 96210284Sjmallett global_opts="${global_opts} -v" 97210284Sjmallettfi 98210284Sjmallett 99210284Sjmallett# Make sure we have a command. 100210284Sjmallettif [ ${#} -lt 1 -o -z "${1:-}" ] ; then 101210284Sjmallett echo "ERROR: No command to hg supplied!" > ${status_output} 102210284Sjmallett usage > ${status_output} 103232812Sjmallettfi 104215990Sjmallett 105210284Sjmallett# grab command 106215990Sjmallettcommand="${1}"; shift 107210284Sjmallett 108210284Sjmallettif [ ${vflag} = "true" ] ; then 109210284Sjmallett echo "# Mercurial command: ${command}" > ${status_output} 110232812Sjmallettfi 111210284Sjmallett 112210284Sjmallett# At this point all command options and args are in "$@". 113210284Sjmallett# Always use "$@" (within double quotes) to avoid breaking 114210284Sjmallett# args with spaces into separate args. 115210284Sjmallett 116232812Sjmallettif [ ${vflag} = "true" ] ; then 117232812Sjmallett echo "# Mercurial command argument count: $#" > ${status_output} 118215990Sjmallett for cmdarg in "$@" ; do 119215990Sjmallett echo "# Mercurial command argument: ${cmdarg}" > ${status_output} 120215990Sjmallett done 121210284Sjmallettfi 122210284Sjmallett 123210284Sjmallett# Clean out the temporary directory that stores the pid files. 124232812Sjmalletttmp=/tmp/forest.$$ 125232812Sjmallettrm -f -r ${tmp} 126232812Sjmallettmkdir -p ${tmp} 127232812Sjmallett 128210284Sjmallett 129210284Sjmallettif [ "${HGFOREST_DEBUG:-false}" = "true" ] ; then 130210284Sjmallett # ignores redirection. 131210284Sjmallett echo "DEBUG: temp files are in: ${tmp}" >&2 132210284Sjmallettfi 133210284Sjmallett 134210284Sjmallett# Check if we can use fifos for monitoring sub-process completion. 135210284Sjmallettecho "1" > ${tmp}/read 136210284Sjmallettwhile_subshell=1 137210284Sjmallettwhile read line; do 138210284Sjmallett while_subshell=0 139210284Sjmallett break; 140210284Sjmallettdone < ${tmp}/read 141210284Sjmallettrm ${tmp}/read 142212844Sjmallett 143212844Sjmalletton_windows=`uname -s | egrep -ic -e 'cygwin|msys'` 144212844Sjmallett 145212844Sjmallettif [ ${while_subshell} = "1" -o ${on_windows} = "1" ]; then 146215014Sjmallett # cygwin has (2014-04-18) broken (single writer only) FIFOs 147215014Sjmallett # msys has (2014-04-18) no FIFOs. 148212844Sjmallett # older shells create a sub-shell for redirect to while 149242104Sjmallett have_fifos="false" 150242104Sjmallettelse 151242104Sjmallett have_fifos="${HGFOREST_FIFOS:-true}" 152242104Sjmallettfi 153212844Sjmallett 154212844Sjmallettsafe_interrupt () { 155212844Sjmallett if [ -d ${tmp} ]; then 156212844Sjmallett if [ "`ls ${tmp}/*.pid`" != "" ]; then 157232812Sjmallett echo "Waiting for processes ( `cat ${tmp}/.*.pid ${tmp}/*.pid 2> /dev/null | tr '\n' ' '`) to terminate nicely!" > ${status_output} 158232812Sjmallett sleep 1 159232812Sjmallett # Pipe stderr to dev/null to silence kill, that complains when trying to kill 160232812Sjmallett # a subprocess that has already exited. 161232812Sjmallett kill -TERM `cat ${tmp}/*.pid | tr '\n' ' '` 2> /dev/null 162232812Sjmallett wait 163232812Sjmallett echo "Interrupt complete!" > ${status_output} 164232812Sjmallett fi 165232812Sjmallett rm -f -r ${tmp} 166232812Sjmallett fi 167210284Sjmallett exit 130 168210284Sjmallett} 169210284Sjmallett 170210284Sjmallettnice_exit () { 171215990Sjmallett if [ -d ${tmp} ]; then 172215990Sjmallett if [ "`ls -A ${tmp} 2> /dev/null`" != "" ]; then 173215990Sjmallett wait 174210284Sjmallett fi 175210284Sjmallett if [ "${HGFOREST_DEBUG:-false}" != "true" ] ; then 176210284Sjmallett rm -f -r ${tmp} 177210284Sjmallett fi 178210284Sjmallett fi 179210284Sjmallett} 180210284Sjmallett 181210284Sjmalletttrap 'safe_interrupt' INT QUIT 182210284Sjmalletttrap 'nice_exit' EXIT 183210284Sjmallett 184210284Sjmallettsubrepos="corba jaxp jaxws langtools jdk hotspot nashorn" 185210284Sjmallettjdk_subrepos_extra="closed jdk/src/closed jdk/make/closed jdk/test/closed hotspot/make/closed hotspot/src/closed hotspot/test/closed" 186210284Sjmallettsubrepos_extra="$jdk_subrepos_extra deploy install sponsors pubs" 187232812Sjmallett 188232812Sjmallett# Only look in specific locations for possible forests (avoids long searches) 189232812Sjmallettpull_default="" 190232812Sjmallettrepos="" 191210284Sjmallettrepos_extra="" 192215990Sjmallettif [ "${command}" = "clone" -o "${command}" = "fclone" -o "${command}" = "tclone" ] ; then 193215990Sjmallett # we must be a clone 194215990Sjmallett if [ ! -f .hg/hgrc ] ; then 195210284Sjmallett echo "ERROR: Need initial repository to use this script" > ${status_output} 196210284Sjmallett exit 1 197210284Sjmallett fi 198210284Sjmallett 199210284Sjmallett # the clone must know where it came from (have a default pull path). 200210284Sjmallett pull_default=`hg paths default` 201210284Sjmallett if [ "${pull_default}" = "" ] ; then 202210284Sjmallett echo "ERROR: Need initial clone with 'hg paths default' defined" > ${status_output} 203210284Sjmallett exit 1 204210284Sjmallett fi 205210284Sjmallett 206210284Sjmallett # determine which sub repos need to be cloned. 207210284Sjmallett for i in ${subrepos} ; do 208210284Sjmallett if [ ! -f ${i}/.hg/hgrc ] ; then 209210284Sjmallett repos="${repos} ${i}" 210232812Sjmallett fi 211232812Sjmallett done 212232812Sjmallett 213232812Sjmallett pull_default_tail=`echo ${pull_default} | sed -e 's@^.*://[^/]*/\(.*\)@\1@'` 214232812Sjmallett 215232812Sjmallett if [ $# -gt 0 ] ; then 216232812Sjmallett if [ "x${1}" = "xjdk" ] ; then 217232812Sjmallett subrepos_extra=$jdk_subrepos_extra 218232812Sjmallett echo "subrepos being cloned are $subrepos_extra" 219232812Sjmallett shift 220232812Sjmallett fi 221232812Sjmallett # if there is an "extra sources" path then reparent "extra" repos to that path 222232812Sjmallett if [ "x${pull_default}" = "x${pull_default_tail}" ] ; then 223232812Sjmallett echo "ERROR: Need initial clone from non-local source" > ${status_output} 224232812Sjmallett exit 1 225232812Sjmallett fi 226232812Sjmallett # assume that "extra sources" path is the first arg 227232812Sjmallett pull_extra="${1}/${pull_default_tail}" 228232812Sjmallett 229232812Sjmallett # determine which extra subrepos need to be cloned. 230232812Sjmallett for i in ${subrepos_extra} ; do 231232812Sjmallett if [ ! -f ${i}/.hg/hgrc ] ; then 232232812Sjmallett repos_extra="${repos_extra} ${i}" 233232812Sjmallett fi 234232812Sjmallett done 235232812Sjmallett else 236232812Sjmallett if [ "x${pull_default}" = "x${pull_default_tail}" ] ; then 237232812Sjmallett # local source repo. Clone the "extra" subrepos that exist there. 238232812Sjmallett for i in ${subrepos_extra} ; do 239232812Sjmallett if [ -f ${pull_default}/${i}/.hg/hgrc -a ! -f ${i}/.hg/hgrc ] ; then 240232812Sjmallett # sub-repo there in source but not here 241232812Sjmallett repos_extra="${repos_extra} ${i}" 242232812Sjmallett fi 243232812Sjmallett done 244232812Sjmallett fi 245232812Sjmallett fi 246232812Sjmallett 247232812Sjmallett # Any repos to deal with? 248232812Sjmallett if [ "${repos}" = "" -a "${repos_extra}" = "" ] ; then 249232812Sjmallett echo "No repositories to process." > ${status_output} 250232812Sjmallett exit 251232812Sjmallett fi 252232812Sjmallett 253232812Sjmallett # Repos to process concurrently. Clone does better with low concurrency. 254232812Sjmallett at_a_time="${HGFOREST_CONCURRENCY:-2}" 255232812Sjmallettelse 256232812Sjmallett # Process command for all of the present repos 257232812Sjmallett for i in . ${subrepos} ${subrepos_extra} ; do 258232812Sjmallett if [ -d ${i}/.hg ] ; then 259232812Sjmallett repos="${repos} ${i}" 260232812Sjmallett fi 261232812Sjmallett done 262232812Sjmallett 263232812Sjmallett # Any repos to deal with? 264232812Sjmallett if [ "${repos}" = "" ] ; then 265232812Sjmallett echo "No repositories to process." > ${status_output} 266232812Sjmallett exit 267232812Sjmallett fi 268232812Sjmallett 269232812Sjmallett # any of the repos locked? 270232812Sjmallett locked="" 271232812Sjmallett for i in ${repos} ; do 272232812Sjmallett if [ -h ${i}/.hg/store/lock -o -f ${i}/.hg/store/lock ] ; then 273232812Sjmallett locked="${i} ${locked}" 274232812Sjmallett fi 275232812Sjmallett done 276232812Sjmallett if [ "${locked}" != "" ] ; then 277232812Sjmallett echo "ERROR: These repositories are locked: ${locked}" > ${status_output} 278232812Sjmallett exit 1 279232812Sjmallett fi 280232812Sjmallett 281232812Sjmallett # Repos to process concurrently. 282232812Sjmallett at_a_time="${HGFOREST_CONCURRENCY:-8}" 283232812Sjmallettfi 284232812Sjmallett 285232812Sjmallett# Echo out what repositories we do a command on. 286232812Sjmallettecho "# Repositories: ${repos} ${repos_extra}" > ${status_output} 287232812Sjmallett 288232812Sjmallettif [ "${command}" = "serve" ] ; then 289232812Sjmallett # "serve" is run for all the repos as one command. 290232812Sjmallett ( 291232812Sjmallett ( 292232812Sjmallett cwd=`pwd` 293210284Sjmallett serving=`basename ${cwd}` 294210284Sjmallett ( 295210284Sjmallett echo "[web]" 296210284Sjmallett echo "description = ${serving}" 297210284Sjmallett echo "allow_push = *" 298232812Sjmallett echo "push_ssl = False" 299232812Sjmallett 300232812Sjmallett echo "[paths]" 301232812Sjmallett for i in ${repos} ; do 302210284Sjmallett if [ "${i}" != "." ] ; then 303210284Sjmallett echo "/${serving}/${i} = ${i}" 304210284Sjmallett else 305210284Sjmallett echo "/${serving} = ${cwd}" 306210284Sjmallett fi 307232812Sjmallett done 308232812Sjmallett ) > ${tmp}/serve.web-conf 309232812Sjmallett 310215990Sjmallett echo "serving root repo ${serving}" > ${status_output} 311215990Sjmallett 312232812Sjmallett echo "hg${global_opts} serve" > ${status_output} 313232812Sjmallett (PYTHONUNBUFFERED=true hg${global_opts} serve -A ${status_output} -E ${status_output} --pid-file ${tmp}/serve.pid --web-conf ${tmp}/serve.web-conf; echo "$?" > ${tmp}/serve.pid.rc ) 2>&1 & 314232812Sjmallett ) 2>&1 | sed -e "s@^@serve: @" > ${status_output} 315232812Sjmallett ) & 316232812Sjmallettelse 317215990Sjmallett # Run the supplied command on all repos in parallel. 318215990Sjmallett 319215990Sjmallett # n is the number of subprocess started or which might still be running. 320215990Sjmallett n=0 321215990Sjmallett if [ ${have_fifos} = "true" ]; then 322215990Sjmallett # if we have fifos use them to detect command completion. 323215990Sjmallett mkfifo ${tmp}/fifo 324232812Sjmallett exec 3<>${tmp}/fifo 325232812Sjmallett fi 326232812Sjmallett 327232812Sjmallett # iterate over all of the subrepos. 328232812Sjmallett for i in ${repos} ${repos_extra} ; do 329232812Sjmallett n=`expr ${n} '+' 1` 330232812Sjmallett repopidfile=`echo ${i} | sed -e 's@./@@' -e 's@/@_@g'` 331232812Sjmallett reponame=`echo ${i} | sed -e :a -e 's/^.\{1,20\}$/ &/;ta'` 332232812Sjmallett pull_base="${pull_default}" 333232812Sjmallett 334232812Sjmallett # regular repo or "extra" repo? 335232812Sjmallett for j in ${repos_extra} ; do 336232812Sjmallett if [ "${i}" = "${j}" ] ; then 337232812Sjmallett # it's an "extra" 338232812Sjmallett if [ -n "${pull_extra}" ]; then 339232812Sjmallett # if no pull_extra is defined, assume that pull_default is valid 340232812Sjmallett pull_base="${pull_extra}" 341232812Sjmallett fi 342232812Sjmallett fi 343232812Sjmallett done 344232812Sjmallett 345232812Sjmallett # remove trailing slash 346232812Sjmallett pull_base="`echo ${pull_base} | sed -e 's@[/]*$@@'`" 347232812Sjmallett 348232812Sjmallett # execute the command on the subrepo 349232812Sjmallett ( 350232812Sjmallett ( 351232812Sjmallett if [ "${command}" = "clone" -o "${command}" = "fclone" -o "${command}" = "tclone" ] ; then 352232812Sjmallett # some form of clone 353232812Sjmallett clone_newrepo="${pull_base}/${i}" 354232812Sjmallett parent_path="`dirname ${i}`" 355232812Sjmallett if [ "${parent_path}" != "." ] ; then 356232812Sjmallett times=0 357232812Sjmallett while [ ! -d "${parent_path}" ] ; do ## nested repo, ensure containing dir exists 358232812Sjmallett if [ "${sflag}" = "true" ] ; then 359232812Sjmallett # Missing parent is fatal during sequential operation. 360232812Sjmallett echo "ERROR: Missing parent path: ${parent_path}" > ${status_output} 361232812Sjmallett exit 1 362232812Sjmallett fi 363232812Sjmallett times=`expr ${times} '+' 1` 364232812Sjmallett if [ `expr ${times} '%' 10` -eq 0 ] ; then 365232812Sjmallett echo "${parent_path} still not created, waiting..." > ${status_output} 366232812Sjmallett fi 367232812Sjmallett sleep 5 368232812Sjmallett done 369232812Sjmallett fi 370232812Sjmallett # run the clone command. 371232812Sjmallett echo "hg${global_opts} clone ${clone_newrepo} ${i}" > ${status_output} 372232812Sjmallett (PYTHONUNBUFFERED=true hg${global_opts} clone ${clone_newrepo} ${i}; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 & 373232812Sjmallett else 374232812Sjmallett # run the command. 375232812Sjmallett echo "cd ${i} && hg${global_opts} ${command} ${@}" > ${status_output} 376232812Sjmallett cd ${i} && (PYTHONUNBUFFERED=true hg${global_opts} ${command} "${@}"; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 & 377232812Sjmallett fi 378210284Sjmallett 379210284Sjmallett echo $! > ${tmp}/${repopidfile}.pid 380210284Sjmallett ) 2>&1 | sed -e "s@^@${reponame}: @" > ${status_output} 381210284Sjmallett # tell the fifo waiter that this subprocess is done. 382210284Sjmallett if [ ${have_fifos} = "true" ]; then 383210284Sjmallett echo "${i}" >&3 384210284Sjmallett fi 385210284Sjmallett ) & 386210284Sjmallett 387210284Sjmallett if [ "${sflag}" = "true" ] ; then 388210284Sjmallett # complete this task before starting another. 389232812Sjmallett wait 390232812Sjmallett else 391232812Sjmallett if [ "${have_fifos}" = "true" ]; then 392232812Sjmallett # check on count of running subprocesses and possibly wait for completion 393232812Sjmallett if [ ${n} -ge ${at_a_time} ] ; then 394232812Sjmallett # read will block until there are completed subprocesses 395232812Sjmallett while read repo_done; do 396210284Sjmallett n=`expr ${n} '-' 1` 397210284Sjmallett if [ ${n} -lt ${at_a_time} ] ; then 398210284Sjmallett # we should start more subprocesses 399210284Sjmallett break; 400210284Sjmallett fi 401210284Sjmallett done <&3 402210284Sjmallett fi 403210284Sjmallett else 404210284Sjmallett # Compare completions to starts 405210284Sjmallett completed="`(ls -a1 ${tmp}/*.pid.rc 2> /dev/null | wc -l) || echo 0`" 406210284Sjmallett while [ `expr ${n} '-' ${completed}` -ge ${at_a_time} ] ; do 407210284Sjmallett # sleep a short time to give time for something to complete 408210284Sjmallett sleep 1 409210284Sjmallett completed="`(ls -a1 ${tmp}/*.pid.rc 2> /dev/null | wc -l) || echo 0`" 410210284Sjmallett done 411232812Sjmallett fi 412215990Sjmallett fi 413232812Sjmallett done 414232812Sjmallett 415232812Sjmallett if [ ${have_fifos} = "true" ]; then 416232812Sjmallett # done with the fifo 417232812Sjmallett exec 3>&- 418232812Sjmallett fi 419215990Sjmallettfi 420210284Sjmallett 421210284Sjmallett# Wait for all subprocesses to complete 422210284Sjmallettwait 423210284Sjmallett 424210284Sjmallett# Terminate with exit 0 only if all subprocesses were successful 425210284Sjmallett# Terminate with highest exit code of subprocesses 426210284Sjmallettec=0 427210284Sjmallettif [ -d ${tmp} ]; then 428210284Sjmallett rcfiles="`(ls -a ${tmp}/*.pid.rc 2> /dev/null) || echo ''`" 429210284Sjmallett for rc in ${rcfiles} ; do 430210284Sjmallett exit_code=`cat ${rc} | tr -d ' \n\r'` 431210284Sjmallett if [ "${exit_code}" != "0" ] ; then 432210284Sjmallett if [ ${exit_code} -gt 1 ]; then 433210284Sjmallett # mercurial exit codes greater than "1" signal errors. 434210284Sjmallett repo="`echo ${rc} | sed -e 's@^'${tmp}'@@' -e 's@/*\([^/]*\)\.pid\.rc$@\1@' -e 's@_@/@g'`" 435210284Sjmallett echo "WARNING: ${repo} exited abnormally (${exit_code})" > ${status_output} 436215990Sjmallett fi 437215990Sjmallett if [ ${exit_code} -gt ${ec} ]; then 438215990Sjmallett # assume that larger exit codes are more significant 439210284Sjmallett ec=${exit_code} 440210284Sjmallett fi 441210284Sjmallett fi 442210284Sjmallett done 443210284Sjmallettfi 444210284Sjmallettexit ${ec} 445210284Sjmallett