October 3, 2007

A script to tell which workstations are using Samba shares

Author: Sergio Gonzalez Duran

A combination of Linux utilities can help you determine who on your network is using which of your shared filesystems at any given time, allowing you to ask those users to log off while you update the system.

One of my clients has many multiple small Visual Basic 6 and Access-based applications running directly on Linux via Samba through network shares on Windows XP clients. Whenever he needs to update the application or database, he asks the users to log off the application so he can upload the new binary application or Access file to the server. However, his mid-sized network has about 100 clients using the apps, so even if only one user is logged on, my client has to get on the phone and go extension by extension until he finds the user who is using the application, ask the user to log off, and perform the upload.

I created a script to solve this problem using a combination of lsof, netstat, gawk, grep, uniq, and other utilities. All of the applications' binaries are stored in /usr/apps, in subfolders named payroll, accounting, sales, invoice, and so on. Assuming, for example, that the client needs to know the IP addresses of all the users who are using the payroll system, you can use the lsof utility, which lists all the open files:

lsof  |  grep /usr/apps/payroll
smbd      16258  systems  cwd   DIR        8,5     4096    2191586 /usr/apps/payroll
smbd      16258  systems  27rR  REG        8,5  2449408    2191760 /usr/apps/payroll/payroll.exe
smbd      16258  systems  28u   REG        8,5 37883904    1684790 /usr/apps/payroll/dbpayroll.mdb
smbd      16258  systems  29uw  REG        8,5      256    1684290 /usr/apps/payroll/dbpayroll.ldb
smbd      19237  systems  cwd   DIR        8,5     4096    2191586 /usr/apps/payroll
smbd      19237  systems  25rR  REG        8,5  2449408    2191760 /usr/apps/payroll/payroll.exe
smbd      19237  systems  27u   REG        8,5 37883904    1684790 /usr/apps/payroll/dbpayroll.mdb
smbd      19237  systems  28uw  REG        8,5      256    1684290 /usr/apps/payroll/dbpayroll.ldb
smbd      19237  systems  29rW  REG        8,5    45056    1684863 /usr/apps/payroll/report1.rpt

The output shows that smbd (Samba) has several files open, from process IDs (PID) 16258 through 19237. You know that two users are using the payroll.exe application, for example. The last line shows that the report1.rpt file is being used. This is a typical Crystal Reports extension, so you can figure out that this application is also busy. However, this doesn't show the users, so you need to extract only the necessary information (the PID numbers) from the previous output:

lsof  |  grep /usr/apps/payroll | gawk '{ print $2 }'
16258
16258
16258
16258
19237
19237
19237
19237
19237

With gawk, you can extract the second field, but you don't need all those repetitions. Use uniq, which removes duplicate lines from a sorted listing:

lsof  |  grep /usr/apps/payroll | gawk '{ print $2 }' | uniq
16258
19237

Send the results to a temporary file:

lsof | grep /usr/apps/payroll | gawk '{ print $2 }' | uniq > tmp

Next, relate those PID numbers with a network socket using the netstat command. With the ââ¬âp option, netstat shows the PID and the name of the program to which each socket belongs:

netstat -p | grep 16258
tcp        0      0 192.168.100.250:netbios-ssn   192.168.100.32:1028          ESTABLISHED 16258/smbd

The fourth column (192.168.100.250:netbios-ssn) is the server machine, and the fifth column (168.100.32:1028) is the IP address and port number of the client machine, which is what you need.

Now you can put everything together by using a script that reads the PIDs in the tmp file:

lsof | grep /usr/apps/payroll | gawk '{ print $2 }' | uniq > tmp
echo "PCs USING THE APPLICATION:"
while read row ; do
   netstat -p | grep $row | gawk '{ print $5 }' | cut -d":" -f1
done 

The line netstat -p | grep $row | gawk '{ print $5 }' | cut -d":" -f1 first executes netstat, then calls grep with the variable $row, which comes from each row of the tmp file. Extract the fifth column with gawk, then with cut and the ":" delimiter, extract the first field, which is the IP address.

Give the script a name, and run chmod 700 script.sh to make it executable. When you run it, the result should look something like this:

./script.sh
PCs USING THE APPLICATION
192.168.100.32
192.168.100.78

Now you just have to tie the IP address to a user name. To do that, add to /etc/hosts lines like this:

192.168.100.32     Lindsay_Hayek
192.168.100.78     Salma_Lohan
192.168.100.145    Tom_Norton
192.168.100.193    Edward_Cruise

And add an additional grep the script to read from /etc/hosts, in the netstat line:

grep `netstat -p | grep $row | gawk '{ print $5 }' | cut -d":" -f1` /etc/hosts

Now, however, if the script finds an IP address that is not listed in /etc/hosts, it will send an error rather than simply print the IP address. We can solve that problem with an if statment. If the above instruction is correct (returns 0 through the variable $?) then the script should go on to the next row of the tmp file, while anything besides 0 means that the IP address wasn't found in /etc/hosts, so just print the IP address. You may be able to find out who it belongs to from a network administrator. The final script would look something like this:

lsof | grep /usr/apps/payroll | gawk '{ print $2 }' | uniq > tmp
echo "PCs USING THE APPLICATION:"
while read row ; do
   grep `netstat -p | grep $row | gawk '{ print $5 }' | cut -d":" -f1` /etc/hosts
   
   if [ $? -ne 0 ]; then
     netstat -p | grep $row | gawk '{ print $5 }' | cut -d":" -f1
   end if
   
done 

And when run, the result would look like:

./script.sh
PCs USING THE APPLICATION
192.168.100.32    Lindsay_Hayek
192.168.100.78    Salma_Lohan
192.168.100.90

You could enhance this script. For example, instead of using payroll in the line lsof | grep /usr/apps/payroll | gawk '{ print $2 }' | uniq > tmp and then having to edit it every time you want to look for another application, you could use something like this:

lsof | grep /usr/apps/$1 | gawk '{ print $2 }' | uniq > tmp
echo "PCs USING THE APPLICATION: $1"

Here, $1 is an argument read from the command line:

./script.sh accounting
PCs USING THE APPLICATION accounting
192.168.100.145    Tom_Norton
192.168.100.178
192.168.100.193    Edward_Cruise

You could also create a menu of applications, or use Zenity to interact with the script in an X Window environment.

Category:

  • System Administration
Click Here!