Send in your Unix questions today! |
See additional Unix tips and tricks
We have looked at the fuser command before to display information about
what users or processes are keeping file systems busy. This command
can save you a lot of time tracking when you need to quickly determine
how a file system or a particular file is being used, particularly when
you cannot unmount a file system due to current usage.
Today, we will look at a script that provides more information on the
processes keeping a file system busy by using fuser to supply process IDs
to ps and ps to then display additional information about each process.
The first thing this particular script does is request that the user
select the file system that he is concerned about. We do this with a
select statement. However, to ensure that we are looking only at locally
mounted file systems, we will pipe our df output through a "grep '^/dev"
command (i.e., we will tell the script to only list entries that start
with "/dev").
Once the user has selected the file system he is interested in, the $FS
parameter contains its name. If he mistakenly types the file system name
instead of the numeric designator from the menu or enters a number that
doesn't correspond to an item in the list, he will get an error and exit
the script.
#!/bin/bash
#
# fbusy: identify processes using file system, uses fuser
echo "select a file system by number:"
select FS in `df -k | grep "^/dev" | awk '{print $NF}'`
do
if [ "$FS" == "" ]; then
echo "invalid selection"
exit 1
fi
break
done
At this point in the script, the user will have selected a file system.
The next thing we do is print a banner including the file system name.
echo Processes using $FS
echo ========================================
We then use the fuser command to find PIDs of processes which are using
the particular file system. Note that we are sending standard error to
/dev/null -- twice. In the parenthesized portion of the syntax, the
use of /dev/null essentially strips the letters (such as "c", "m", "o"
and such that tell how each process is using the file system) from
fuser's output, thus providing the ps command with a clean list of PIDs.
The second use of /dev/null keeps our users from having to look at an
ugly set of errors if the fuser command finds no processes are running
and then presents an empty process list of ps.
The ps command uses a -o option to select a series of process variables
we want to see and retrieves this information for all the processes located
by fuser.
ps -o uid,pid,ppid,tty,time,args -p "$(fuser -c $FS 2>/dev/null)" 2>/dev/null
Running this script, you would see something like this:
boson> ./fbusy
select a file system:
1) / 3) /usr/local 5) /mail
2) /export/home 4) /var/spool/mqueue 6) /opt
#? 2
Processes using /export/home
========================================
UID PID PPID TT TIME COMMAND
1111 28414 26208 pts/2 0:00 /bin/bash ./fbusy
1111 26208 26206 pts/2 0:00 -bash
5026 22948 22947 pts/3 0:00 telnet 66.77.88.99
5026 22947 22945 pts/3 0:00 -bash
In this example, we can see that four processes are keeping /export/home
busy. We also see the UID, parent process and terminal, process time
and the particular command that is being run.
We lose some information by sending fuser's standard error to /dev/null.
The letters ("c" and such) that were sent to the bit bucket could help
the user understand how each of the particular process is busying the
particular file system. For example, "c" indicates the process is using
the file system as its current directory while "m" indicates that the
process is using a file mapped with mmap.
One way to provide some sense of how the file system is being busied
would be to insert code like this before the existing fuser command:
PROCS=`fuser -c $FS 2>/tmp/$$`
if [ "$PROCS" == "" ]; then
echo None
exit
fi
The fuser command is code (yes, we duplicating effort here) would store
the letter codes associated with fuser output in a temporary file which
could later be displayed and then removed like this:
cat /tmp/$$
rm /tmp/$$
This code also checks to see if the PID list is empty, prints "None" and
exits the script if it is. This would allow us to drop the second
redirecting of standard error to /dev/null in our original fuser command.
A simple, and perhaps more useful way (especially if we're already willing
to run the fuser command a second time), is to simply add this fuser
command to the end of the script:
# fuser -c $FS
This command will print out the PIDs along with the associated letter codes:
/export/home: 28414co 26208c 22948c 22947c
The user can then compare this information with the process listing already
shown. If you do this, you might also want to provide the following
explanation from the fuser man command to make it easy for your users to
interpret the codes:
c Indicates that the process is using the file as its
current directory.
m Indicates that the process is using a file mapped with
mmap(2). See mmap(2) for details.
o Indicates that the process is using the file as an
open file.
r Indicates that the process is using the file as its
root directory.
t Indicates that the process is using the file as its
text file.
y Indicates that the process is using the file as its
controlling terminal.