Skip to content

Instantly share code, notes, and snippets.

@phoenix24
Forked from zaius/background.sh
Created January 17, 2011 04:44
Show Gist options
  • Save phoenix24/782505 to your computer and use it in GitHub Desktop.
Save phoenix24/782505 to your computer and use it in GitHub Desktop.
ctrl-z
bg
touch /tmp/stdout
touch /tmp/stderr
gdb -p $!
# In GDB
p dup2(open("/tmp/stdout", 1), 1)
p dup2(open("/tmp/stderr", 1), 2)
detach
quit
# Back in shell
disown
logout
@phoenix24
Copy link
Author

!/bin/bash

THIS=$0
ARGS=$@
name=$(basename $THIS)

usage () {
cat <<EOF
Usage: $name: redirect input/output/error of a running process
Two ways to run this program:
$name [-q][-o ] [-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/stdin >/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

if gdb -batch -n -x $GDBCMD >/dev/null </dev/null; then
[ "$quiet" != "yes" ] && echo "Success" >&2
else
warn "Remapping failed"
fi

cp $GDBCMD /tmp/gdbcmd

rm -f $GDBCMD

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment