Skip to content

Instantly share code, notes, and snippets.

@jmdeldin
Created March 24, 2012 20:28
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 jmdeldin/2187521 to your computer and use it in GitHub Desktop.
Save jmdeldin/2187521 to your computer and use it in GitHub Desktop.
dupx for OS X 10.7.3

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!

#!/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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment