public
Created

dupx for OS X 10.7.3

  • Download Gist
README.md
Markdown

dupx is a shell script to redirect an existing process STDOUT or STDIN to a file. Unfortunately, I was getting this error after invoking dupx -o /tmp/some.log PID:

/tmp/gdbcmd.F8Qf:5: Error in sourced command file:
Unable to call function "dup2" at 0x7fff96e233b8: no return type information available.
To call this function anyway, you can cast the return type explicitly (e.g. 'print (float) fabs (3.0)')
Success

I found a handy Gist that did mostly what dupx does, toyed with it in GDB, and then modified the original dupx script to add (int) type casting in the open, dup, dup2, close, and write calls.

Usage

# create some script and forget to log STDOUT to a file
sh -c 'while [ 1 ]; do echo foo && sleep 2; done'

# get the PID of the above command with pgrep, or...
pid=ps u | grep '[f]oo' | awk '{print $2}'

# call dupx
dupx -o /tmp/my_new.log $pid

tail -f /tmp/my_new.log

Thanks to mteckert for pointing me to dupx in the first place!

dupx.sh
Shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
#!/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=(int) open(\"$_name\", $_mode)"
echo "set \$xd=(int) dup($_desc)"
echo "call (int) dup2(\$fd, $_desc)"
echo "call (int) close(\$fd)"
if [ $((_mode & 3)) ] && [ -n "$_msgs" ]; then
_len=$(echo -en "$_msgs" | wc -c)
echo "call (int) write(\$xd, \"$_msgs\", $_len)"
fi
echo "call (int) 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

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.