Created
December 1, 2019 16:26
-
-
Save jeremyxu2010/fdfe20d937cabe0f1c40c062b8918e33 to your computer and use it in GitHub Desktop.
dupx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 <outfile>] [-e <errorfile>] [-i <inputfile>] [-n <fd>:<filename> ] <pid> | |
or simply: | |
$name [-q] <pid> | |
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 <stdin> specify filename of the new standard input | |
-o <stdout> specify filename of the new standard output | |
-e <stderr> specify filename of the new standard error | |
-n <fd>:<filename> 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