Last active
September 24, 2020 22:28
-
-
Save mhelsley/848c0e1e0681b65aedb269a949ba0b97 to your computer and use it in GitHub Desktop.
Swap file descriptors in running processes
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 | |
# | |
# 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