Skip to content

Instantly share code, notes, and snippets.

@mhelsley
Last active September 24, 2020 22:28
Show Gist options
  • Save mhelsley/848c0e1e0681b65aedb269a949ba0b97 to your computer and use it in GitHub Desktop.
Save mhelsley/848c0e1e0681b65aedb269a949ba0b97 to your computer and use it in GitHub Desktop.
Swap file descriptors in running processes
#!/bin/bash
#
# fdswap
# Based on https://www.redpill-linpro.com/sysadvent/2015/12/04/changing-a-process-file-descriptor-with-gdb.html
# Alternatives https://github.com/bewakes/fdswap
# Updated by
# anonymous friend of mhelsley
# mhelsley
# defanging, error handling, GDB verbosity/UI quirks, reduced appearance of magic numbers
#
if [ "$2" = "" ]; then
echo "
Usage: $0 /path/to/oldfile /path/to/newfile [pids]
Example: $0 /var/log/daemon.log /var/log/newvolume/daemon.log 1234
Example: $0 /dev/pts/53 /dev/null 2345"; exit 0
fi
if gdb --version > /dev/null 2>&1; then true
else echo "Unable to find gdb."; exit 1
fi
src="$1"; dst="$2"; shift; shift
pids=$*
# Set to non-empty to enable debugging this script
DEBUG_FDSWAP=""
# Hard-coded Linux open flags -- a more portable way to mask the flags would be to use a compiler
# but we know we're on Linux because we're going to be using /proc/$pid/fdinfo
O_TRUNC=$(( 01000 ))
O_CREAT=$(( 0100 ))
O_EXCL=$(( 0200 ))
O_DIRECTORY=$(( 0200000 ))
O_TMPFILE=$(( 020000000 | O_DIRECTORY ))
O_NOCTTY=$(( 0400 ))
FLMASK=$(( ~(O_CREAT|O_EXCL|O_TRUNC|O_TMPFILE) ))
for pid in ${pids:=$( /sbin/fuser $src | cut -d ':' -f 2 )};
do
echo "src=$src, dst=$dst"
echo "$src has $pid using it"
(
echo "set confirm off"
echo "set pagination off"
if [ -n "${DEBUG_FDSWAP}" ]; then
echo "set trace-commands on"
echo "set logging on"
else
echo "set width 0"
echo "set height 0"
echo "set verbose off"
fi
echo "attach $pid"
#echo "\n\n"
for ufd in $(LANG=C ls -l /proc/$pid/fd | \
grep "$src"\$ | awk ' { print $9; } ');
do
offset=$(( $(grep -E '^pos:' /proc/$pid/fdinfo/$ufd | cut -d : -f 2) ))
# The original fdswap script's flags, 66, correspond to 0102 or O_CREAT|O_RDWR.
# Let's use the flags of the file descriptor we're replacing (e.g. O_RDONLY) but
# limit them so we don't indirectly create anything new or clobber anything that
# already exists.
flags=$(( $(grep -E '^flags:' /proc/$pid/fdinfo/$ufd | cut -d : -f 2) ))
(( flags &= FLMASK )) # Don't create/clobber things in the filesystem
(( flags |= O_NOCTTY )) # Don't change the process' controlling terminal
# Emit GDB script snippet
cat - <<-EOFOEOF
define die
detach
quit
end
# Set mode to 0 so any created files have no permissions
call open("$dst", $flags, 0)
set \$tmpfd = \$
die if \$tmpfd < 0
# Only adjust the offset if we have to
call(off64_t) lseek64(\$tmpfd, 0, 1) # Read current offset using SEEK_CUR
if \$ != $offset
call(off64_t) lseek64(\$tmpfd, $offset, 0) # Set offset using SEEK_SET
die if \$ != $offset
end
call(int) dup2(\$tmpfd, $ufd)
die if \$ != $ufd
call close(\$tmpfd)
EOFOEOF
done
echo 'detach'
echo 'quit'
) | gdb -q -batch
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment