Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

185
LINES

< > BotCompany Repo | #1033366 // dupx

Document

#!/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

cat $GDBCMD
echo
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

download  show line numbers   

Travelled to 2 computer(s): bhatertpkbcr, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1033366
Snippet name: dupx
Eternal ID of this version: #1033366/2
Text MD5: 65ac9705493e4d2ccbefa5218d8a645a
Author: stefan
Category:
Type: Document
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-10-25 05:48:55
Source code size: 5281 bytes / 185 lines
Pitched / IR pitched: No / No
Views / Downloads: 149 / 55
Version history: 1 change(s)
Referenced in: [show references]