BASH Script On Raspberry Pi To Show Filehandles

Solved an interesting problem on Unix recently that I automated with the shell; which I will demonstrate on Raspberry Pi

QUICK BACKGROUND

I performed a logrotate on a log file which performed the following transformation:

service.log => service.log.1

which is pretty straight forward, but here was the problem:

After logrotate did its thing, logs were being written to the archived log file service.log.1 and NOT service.log.

This of course defeats to whole purpose of logrotate, so to begin troubleshooting, I needed to answer this question:

What process (or processes) are keeping a handle on that file being logrotated?

MY METHOD

So my first step was to figure out what command I could start with to answer my question.

After a quick google search and testing, I found fuser to be a great spring board.

I began my testing by using the common binary file /usr/sbin/sshd:

root@raspberrypi:~# fuser /usr/sbin/sshd
/usr/sbin/sshd: 406e 776e 846e

Now, according to man 1 fuser, it:

outputs only the PIDs to stdout, everything else is sent to stderr.


Hence, the following will clean up the original output a bit:

root@raspberrypi:~# fuser /usr/sbin/sshd 2> /dev/null
406 776 846root@raspberrypi:~#

(No newline is printed at the end of the output so the prompt was returned back on the same line.)


Now, if we pipe the output to xargs, it will work and give use the information we want. But not in most user-friendly format:

root@raspberrypi:~# fuser /usr/sbin/sshd 2> /dev/null | xargs -n1 ps -p
PID TTY TIME CMD
406 ? 00:00:00 sshd
PID TTY TIME CMD
776 ? 00:00:00 sshd
PID TTY TIME CMD
846 ? 00:00:00 sshd

Not too pretty.

Reason being, it ran a ps -p on EACH PID.

Instead, we want to run a single ps -p on ALL PIDs at once:

Here’s a way to run ps -p on ALL PIDS at once (-f added to ps command):

root@raspberrypi:~# ps -p $(fuser /usr/sbin/sshd 2> /dev/null | tr -s ' ' | sed -e 's/ /,/g' -e 's/^,//') -f
UID PID PPID C STIME TTY TIME CMD
root 406 1 0 22:30 ? 00:00:00 /usr/sbin/sshd -D
root 776 406 0 22:31 ? 00:00:00 sshd: pi [priv]
pi 846 776 0 22:31 ? 00:00:00 sshd: pi@pts/0

Let’s pause here and make a couple of quick observations:

  • The end result is getting more useful
  • But getting there (the code) is starting to outgrow the shell
  • And, its not easy to re-run this code on ANY filename

Enter…

SCRIPTING

If we “parameterize” the filename AND make it a script, the end result will look something like this:

root@raspberrypi:~/Documents# sh ./list_filehandles.sh /usr/sbin/sshd

Here is the source code for ./list_processhandles.sh:

#!/bin/sh

echo "******************************************"
echo "*"
echo -n "* Parameter passed in: "
echo $1
echo "*"
echo "******************************************"

export MYFILE=$1

ps -p $(fuser $MYFILE 2> /dev/null  | tr -s ' ' | sed -e 's/ /,/g' -e 's/^,//') -f

Now we can fuser ANY file we want to investigate (30 second demo below):

sh ./list_filehandles.sh <filename>