Samba共有ファイルを使用中のマシンがわかるスクリプト

 Linuxユーティリティを組み合わせて使えば、ネットワーク上の誰がどの共有ファイルシステムを利用しているかがいつでもわかるので、システムのアップデート時には彼らにログオフを依頼することができる。

 私の顧客の1人は、Visual Basic 6およびAccessベースの数多くの小型アプリケーションを直接Linux上で稼働させて、Sambaによるネットワーク共有を介して各Windows XPクライアントから利用できるようにしている。だが、そうしたアプリケーションやデータベースのアップデートが必要になるたびに、彼は新しいバイナリやAccessファイルをサーバにアップロードできるようにそのアプリケーションからのログオフをユーザに依頼することになる。しかし、彼の管理する中規模ネットワークにはそうしたアプリケーションを使用しているクライアントが100台ほど存在するため、たとえログオンしているのが1人だけでも、そのアプリケーションを使用中のユーザが見つかるまで延々と電話をかけ続け、見つかったユーザにログオフを依頼して、アップロードを実行しなければならない。

 今回、この問題をlsofnetstatgawkgrepuniqをはじめとするユーティリティの組み合わせによって解決するスクリプトを作成した。アプリケーションのバイナリはすべて、/usr/appsの下にある、payroll、accounting、sales、invoiceといったサブフォルダに格納されている。たとえば、先ほどの顧客が給与台帳(payroll)システムを使用中のすべてのユーザを知る必要があるなら、以下に示すように、開かれている全ファイルを表示してくれるlsofユーティリティが利用できる。

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

 この出力のプロセスID(PID)が16528から19237までの部分は、smbd(Samba)によって複数のファイルが開かれていることを示している。たとえば、2人のユーザがpayroll.exeアプリケーションを使用中であることがわかる。また、最後の行は、report1.rptファイルが使用中であることを示している。これは標準的なCrystal Reportの拡張子であり、このアプリケーションもビジー状態であることがわかる。しかし、こちらはユーザ名がわからないので、以下のようにして先ほどの出力結果から必須の情報(PID番号)だけを抽出する必要がある。

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

 このようにgawkを使えば2番目のフィールドだけを抜き出せるが、重複するものは必要ない。そういうときは、ソートされた一覧から重複する行を削除してくれるuniqを使えばよい。

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

 この結果をテンポラリ(tmp)ファイルに残すには、次のようにする。

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

 続いて、netstatコマンドを使ってこれらのPID番号をネットワークソケットに関連付ける。次のようにnetstat-pオプションを指定すると、各ソケットが属するプログラムのPIDと名前が表示される。

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

 4番目のカラム(192.168.100.250:netbios-ssn)はサーバマシンを、5番目のカラム(168.100.32:1028)はクライアントマシンのIPアドレスおよびポート番号を示している。ここで必要なのは5番目のカラムのほうである。

 今度は、PIDをtmpファイルに記録する処理をすべて1つのスクリプトにまとめてみよう。

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 < tmp

netstat -p | grep $row | gawk '{ print $5 }' | cut -d":" -f1という行は、まずnetstatを実行してから$rowに対するgrepを呼び出す。この変数$rowには、tmpファイルのそれぞれの行が順に入る。続いてgawkで5番目のカラムを抜き出したあと、cutでデリミタ”:”を除いたうえでIPアドレスにあたる最初のフィールドを取り出す。

 このスクリプトに名前を付けたら、chmod 700 script.shによって実行ファイルにする。スクリプトの実行結果は、次のようになるはずだ。

./script.sh
PCs USING THE APPLICATION
192.168.100.32
192.168.100.78

 あとはIPアドレスとユーザ名の関連付けを行うだけでよい。そのために、以下のような行を/etc/hostsに追記する。

192.168.100.32     Lindsay_Hayek
192.168.100.78     Salma_Lohan
192.168.100.145    Tom_Norton
192.168.100.193    Edward_Cruise

 また、この/etc/hostsを参照するためのgrepを、先ほどのスクリプトのnetstatの行に加えて次のようにする

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

 しかしこのままでは、/etc/hostsに記されていないIPアドレスが見つかった場合、このスクリプトはエラーを返し、単純にそのIPアドレスを表示してはくれない。この問題はif文を使えば解決できる。上記の命令が正しいものであれば(変数$?に0が返される)、スクリプトはtmpファイルの次の行に進む。しかし、0以外の値が返された場合は該当するIPアドレスが/etc/hostsに見つからなかったということなので、単純にそのIPアドレスを表示するようになっている。あとでネットワーク管理者に訊けば、そのIPアドレスが誰のものかわかるかもしれない。ここで、最終的なスクリプトを以下に示す。

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 < tmp

 実行すると、次のような結果が得られる。

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

 このスクリプトはさらに拡張することもできる。たとえば、lsof | grep /usr/apps/payroll | gawk '{ print $2 }' | uniq > tmpの行でpayrollと指定してしまうと対象とするアプリケーションが変わるたびにその部分を編集しなければならなくなるので、次のようにするとよい。

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

 ここで$1はコマンドラインから渡す引数であり、次のようにして指定する。

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

 また、アプリケーションのメニューを作成したり、Zenityを使ってXウィンドウ環境での対話型スクリプトにしたりすることも可能だろう。

  Sergio Gonzalez DuranはLinux管理者やシステム開発者、またネットワークセキュリティのカウンセラーとして活動しながら、各種Linux講座での指導や、Linuxおよびオープンハウス関連のスペイン語WebサイトLinuxtotal.com.mxの公開も手がけている。

linux.com 原文