Skip to content

Instantly share code, notes, and snippets.

@basinilya
Last active July 27, 2018 09:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save basinilya/9320b806efacece314c2ad16524993c1 to your computer and use it in GitHub Desktop.
Save basinilya/9320b806efacece314c2ad16524993c1 to your computer and use it in GitHub Desktop.
makelog.sh
#!/bin/bash
# usage: ./makelog.sh -m make.log /usr/bin/make all
startdate=$(date +"%F %T.%3N%z")
makemode=
servermode=
target=
prereqs=()
while [ $# != 0 ]; do
case $1 in
'--')
shift
break
;;
'-s')
servermode=x
shift
;;
'-m')
makemode=x
export MAKELOG=${2:?}
shift 2
;;
'-t')
target=${2:?}
shift 2
;;
'-p')
prereqs+=("${2:?}")
shift 2
;;
'-c')
break
;;
-*)
>&2 echo "unknown option $1"
exit 1
;;
*)
break
;;
esac
done
if [ "$servermode" ]; then
exec 2>&4
trap '' HUP # although we are detached, parent bash may still HUP us
cat >"${MAKELOG:?}" # wait pipe EOF
>&2 printf '\nfinished\n'
exit 0
fi
if [ "$makemode" ]; then
set -e
THISDIR=$(cd -- "$(dirname -- "$MAKELOG")" && pwd)
THISFILE=${MAKELOG##*/}
export MAKELOG_LOCK="$THISDIR/$THISFILE.lck"
THISDIR=$(cd -- "$(dirname -- "$0")" && pwd)
THISFILE=${0##*/}
exec 4>&2
# create pipe handle to share across all processes
coproc (
read s # to avoid race condition do not die until we disown/do stuff with $COPROC
# detach from tty
setsid "$THISDIR/$THISFILE" -s <&0 # create grandchild and die
)
disown
export MAKELOG_FD=${COPROC[1]}
# clear FD_CLOEXEC for write side of the pipe and unblock the child
eval "exec ${COPROC[0]}>&-; exec ${COPROC[0]}>&1 1>&${COPROC[1]} ${COPROC[1]}>&-; exec ${COPROC[1]}>&1 1>&${COPROC[0]} ${COPROC[0]}>&-; echo >&${COPROC[1]}"
export MAKELOG_STOP_AT_PID=$$
exec -- "$@" SHELL="$THISDIR/$THISFILE"' $(@:%=-t %) $(^:%=-p %) --'
fi
fn_now() {
# linux,cygwin,msys
read ms x </proc/uptime
ms=${ms/./}0
}
fn_now
old_ms=$ms
# output consists of name:value pairs and lists
# the colon is chosen to not interfere with the equal sign in environ
# each token is separated by nul character
# values may contain newlines
# lists with unknown number of elements end with a name:value pair with an empty name
# a whole record also ends with a name:value pair with an empty name, but the value contains double newline for cosmetic purpose
LF='
'
pid=$$
${MAKELOG_SHELL:-/bin/sh} "$@"
ret=$?
set -e
exec 41>>"${MAKELOG_LOCK:?}"
flock 41
exec 1>&"${MAKELOG_FD:?}"
printf 'date:%s\0' "$startdate"
printf 'target:%s\0' "$target"
# nprereqs followed by n prereqs delimited by nul
printf 'nprereqs:%s\0' ${#prereqs[@]}
if [ ${#prereqs[@]} != 0 ]; then
printf '%s\0' "${prereqs[@]}"
fi
printf 'cwd:%s\0' "$PWD"
# TODO: currently, args are always: -c "cmd1 && cmd2 && cmd3 && ..."
# nargs followed by n args delimited by nul
printf 'nargs:%s\0' $#
if [ $# != 0 ]; then
printf '%s\0' "$@"
fi
# process hierarchy list with unknown number of elements; terminated with just a colon
savepid=$pid
printf 'pidstack:\0'
# in /proc/self/status command can contain spaces, but newlines are escaped
while true; do
name=
ppid=
shouldbreak=": : break"
while read -r key val; do
case $key in
'Name:')
name=$val
;;
'PPid:')
ppid=$val
;;
*) continue;;
esac
shouldbreak=${shouldbreak:2}
$shouldbreak
done </proc/${pid:?}/status
printf '%s:%s\0' "${pid}" "$name"
[ 0 != "$ppid" -a x"$MAKELOG_STOP_AT_PID" != "x$pid" ] || break
pid=$ppid
done
printf ':\0'
pid=$savepid
printf 'environ:\0'
# env var list with unknown number of elements; terminated with just a colon
cat /proc/${pid:?}/environ
printf ':\0'
fn_now
ms=$((ms - old_ms))
printf 'millis:%s\0' $ms
printf 'ret:%s\0' $ret
printf ':\n\n\0'
exit $ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment