#! /bin/sh # # [sh -x] spawn [command [argument ...]] # # export id to create&destroy or reuse the working directory //proc/$id/. # this feature is for debug only and marked as deprecated, so don't rely # on it too hard. # spawn() { set -euf # establish working subdirectory in //proc. we're mking only # transient dirs, i.e. if we mkdir, then we also defer rmdir. if test -n "${id-}"; then : "using id=$id from env" wd=$pd/$id if ! test -d $wd; then : "make transient $wd/" mkdir $wd defer rmdir $wd elif ! test `ls $wd | wc -l` = 0; then : "$wd/ is not empty!" exit 23 else : "reuse existing $wd/" fi else id=`cd $pd && mktemp -d XXXXXXXXXXXXXXXX` wd=$pd/$id defer rmdir $wd : "made transient $wd/" fi # change to //proc working directory cwd="$PWD" cd $wd defer cd $cwd # create named pipes for the child process's stdio mkfifo 0 1 2 defer rm 0 1 2 # spawn child process ( : "in $PWD/ spawn ${*:-nothing}" set +x # disable debug output so we don't clobber 2 exec 0>&- 1>&- 2>&- 0<>0 1<>1 2<>2 cd "$cwd" exec "$@") & pid=$! # setup a trap to kill the child process if this (parent) process dies defer kill $pid # store misc. info. ln -snf $cwd cwd echo $id >id echo $$ >ppid echo $pid >pid defer rm cwd id pid ppid # wait for the child process's set +e wait $pid code=$? set -e # the child is already dead cancel kill $pid # return the same way wait did (exit $code) } # # defer [command [argument ...]] # # Defer execution of a command. Deferred commands are executed in LIFO # order immediately before the script terminates. See (golang's defer # statement for more information how this should work). # defer() { defer="$*${defer+ $defer}" } # # cancel [command [argument ...]] # # Cancel a deferred command. The arguments have to match exactly a # prior defer call or else chaos and mayhem shall haunt thee and shi- # cancel() { defer="`echo "$defer" | grep -Fxv "$*"`" } # setup //proc directory pd=/tmp/krebs/proc mkdir -p $pd test -w $pd # setup deferred execution and spawn command trap 'eval "${defer-}"; defer=' EXIT INT TERM spawn "$@"