#!/bin/bash THIS=$0 ARGS=$@ name=$(basename $THIS) usage () { cat <] [-e ] [-i ] [-n : ] or simply: $name [-q] The first form files specified in -o, -e, and -i options will become standard output, standard error, and standard input. At least one option must be specified, or second form will be assumed. For example, using Bash syntax: bash -c 'sleep 1m && echo "rise and shine"' & $name -o /tmp/test \$! The same with explicit stdout descriptor number (1) using -n option: bash -c 'sleep 1m && echo "rise and shine"' & $name -n 0:/tmp/test \$! In the second form, current in/out/err are remapped into the process. For example, using Bash syntax: bash -c 'sleep 1m && echo "rise and shine"' & $name \$! >>/tmp/test These examples should be equivalent (i.e. -o foo will append to file foo, if it already exists) with respect to stdout, but in the second stdin and stderr are remapped as well. Option (-n) allows explicit specification of file descriptors. It can be given multiple times, e.g.: $name -n 0:/tmp/stdin -n 1:/tmp/stdout -n 2:/tmp/stderr PID is equivalent to, using Bash syntax: $name PID /tmp/stdout 2>/tmp/stderr All files specified in (-n) option are opened in read/write & append mode. Summary of options: -i specify filename of the new standard input -o specify filename of the new standard output -e specify filename of the new standard error -n : specify descriptor number and filename to remap to -q be quiet -h this help EOF } warn () { echo "$name: $@" >&2 } # XXX add multiple -n option quiet="no" nopt="" while getopts "ho:e:i:qn:" opt; do case $opt in ( o ) stdout=$OPTARG; ;; ( e ) stderr=$OPTARG; ;; ( i ) stdin=$OPTARG; ;; ( n ) nopt="$nopt $OPTARG"; ;; ( q ) quiet="yes"; ;; ( * ) usage; exit 0; ;; esac done shift $((OPTIND-1)) if [ $# != 1 ]; then usage exit 1 fi if [ -n "$stdout" ] && ! 2>/dev/null : >> $stdout ; then warn "Cannot write to (-o) $stdout" exit 1 fi if [ -n "$stderr" ] && ! 2>/dev/null : >> $stderr ; then warn "Cannot write to (-e) $stderr" exit 1 fi if [ -n "$stdin" ] && ! 2>/dev/null : >> $stdin ; then warn "Cannot write to (-i) $stdin" exit 1 fi fds="" if [ -n "$nopt" ]; then for n_f in $nopt; do n=${n_f%%:*} f=${n_f##*:} if [ -n "${n//[0-9]/}" ] || [ -z "$f" ]; then warn "Error parsing descriptor (-n $n_f)" exit 1 fi if ! 2>/dev/null : >> $f; then warn "Cannot write to (-n $n_f) $f" exit 1 fi fds="$fds $n" fns[$n]=$f done fi if [ -z "$stdout" ] && [ -z "$stderr" ] && [ -z "$stdin" ] && [ -z "$nopt" ]; then #second invocation form: dup to my own in/err/out [ -e /proc/$$/fd/0 ] && stdin=$(readlink /proc/$$/fd/0) [ -e /proc/$$/fd/1 ] && stdout=$(readlink /proc/$$/fd/1) [ -e /proc/$$/fd/2 ] && stderr=$(readlink /proc/$$/fd/2) if [ -z "$stdout" ] && [ -z "$stderr" ] && [ -z "$stdin" ]; then warn "Could not determine current standard in/out/err" exit 1 fi fi PID=$1 if ! 2>/dev/null kill -0 $PID; then warn "Error accessing PID $PID" exit 1 fi if [ "$quiet" != "yes" ]; then msg_stdout="Remaining standard output of $PID is redirected to $stdout\n" msg_stderr="Remaining standard error of $PID is redircted to $stderr\n" fi gdb_cmds () { local _name=$1 local _mode=$2 local _desc=$3 local _msgs=$4 local _len [ -w "/proc/$PID/fd/$_desc" ] || _msgs="" if [ -d "/proc/$PID/fd" ] && ! [ -e "/proc/$PID/fd/$_desc" ]; then warn "Attempting to remap non-existent fd $n of PID ($PID)" fi [ -z "$_name" ] && return echo "set \$fd=open(\"$_name\", $_mode)" echo "set \$xd=dup($_desc)" echo "call dup2(\$fd, $_desc)" echo "call close(\$fd)" if [ $((_mode & 3)) ] && [ -n "$_msgs" ]; then _len=$(echo -en "$_msgs" | wc -c) echo "call write(\$xd, \"$_msgs\", $_len)" fi echo "call close(\$xd)" } trap '/bin/rm -f $GDBCMD' EXIT GDBCMD=$(mktemp /tmp/gdbcmd.XXXX) { #Linux file flags (from /usr/include/bits/fcntl.sh) O_RDONLY=00 O_WRONLY=01 O_RDWR=02 O_CREAT=0100 O_APPEND=02000 echo "#gdb script generated by running '$0 $ARGS'" echo "attach $PID" gdb_cmds "$stdin" $((O_RDONLY)) 0 "$msg_stdin" gdb_cmds "$stdout" $((O_WRONLY|O_CREAT|O_APPEND)) 1 "$msg_stdout" gdb_cmds "$stderr" $((O_WRONLY|O_CREAT|O_APPEND)) 2 "$msg_stderr" for n in $fds; do msg="Descriptor $n of $PID is remapped to ${fns[$n]}\n" gdb_cmds ${fns[$n]} $((O_RDWR|O_CREAT|O_APPEND)) $n "$msg" done #echo "quit" } > $GDBCMD cat $GDBCMD echo if gdb -batch -n -x $GDBCMD >/dev/null &2 else warn "Remapping failed" fi #cp $GDBCMD /tmp/gdbcmd rm -f $GDBCMD