UNIX ドメインソケットの接続対応表を作る

明けましておめでとうございます。masm11 です。

UNIX ドメインソケットってご存知ですか? 知らないけど実は使ってる (設定したことがある) 方も結構いらっしゃるかもしれません。 今回は、UNIX ドメインソケットをちょっとだけ便利にする自作ツールを一つご紹介したいと思います。

UNIX ドメインソケットとは?

まず、インターネットドメインといえば、TCP や UDP が有名ですね。 TCP はストリーム型で、つまり、送った連続データがそのまま受け取れます。また、信頼性があると言われています。UDP はデータグラム型で、つまり、一度に送れる量に制限はあるものの、一度で送ったデータは一度で受け取れます。

インターネットドメインの TCP や UDP に対して、UNIX ドメインには名前はありませんが、こちらにもストリーム型やデータグラム型があります。

では、UNIX ドメインソケットはどこで使われているのでしょうか?

nginx に以下のような設定をしたことはありませんか?

upstream app {
    server unix:/tmp/unicorn.sock;
}

unicorn には以下のように設定していますね。

listen '/tmp/unicorn.sock'

これらは /tmp/unicorn.sock を使って nginx と unicorn を接続する設定です。これが UNIX ドメインソケットです。

ls で見てみると、以下のように先頭が s になっていて、 ソケットであることを示しています。

[ec2-user@ip-172-31-30-9 ~]$ ls -al /tmp/unicorn.sock 
srwxrwxrwx 1 ec2-user ec2-user 0 1016 10:31 /tmp/unicorn.sock
[ec2-user@ip-172-31-30-9 ~]$ 

UNIX ドメインソケットは、ファイルシステム中にソケットを作り、 そのソケットを使って2つのプロセスがつながります。 従って、同じコンピュータ内でしか接続できません。

何と何がつながっている?

では、今、私のコンピュータにはどんな UNIX ドメインソケットがあるのでしょうか?

それを見るには ss というコマンドを使います。以前は netstat というコマンドが使われていましたが、Linux ではここ数年で ss コマンドに置き換わりました。

以下のように実行してみてください。

ss -anp | grep ^u_str

UNIX ドメインのストリーム型ソケットの一覧が表示されます。

例えば以下の行を見てみます (見づらいので空白は適当にまとめています)。

u_str  ESTAB  0 0  @/tmp/ibus/dbus-PSq26h33 204929  * 203227  users:(("ibus-daemon",pid=607643,fd=12))
  • u_str

    UNIX ドメインのストリーム型ソケットです。

  • ESTAB

    接続しています。

  • 0 0

    送受信バッファに溜まっているデータ量を示します。

  • @/tmp/ibus/dbus-PSq26h33 204929

    自プロセスが使っている側のソケットについての情報です。 (先頭が @ で始まっているものは、ファイルシステム中には存在しません)

  • * 203227

    接続相手側のソケットについての情報です。

  • users:(("ibus-daemon",pid=607643,fd=12))

    自プロセスについての情報です。

ibus-daemon が使っているソケットですね。

では接続相手は何者でしょうか? そう思った時は、まず * 203227 の部分を見ます。接続相手側のソケットについての情報、ですね。 これで検索してみると、ありました。

u_str  ESTAB  0 0  * 203227  * 204929  users:(("wf-panel",pid=607640,fd=19))

相手側プロセスは wf-panel であることが判ります。

ツールを作る

さて、インターネットドメインの場合は、相手側プロセスは別のコンピュータにあることが極普通なので、それがどんなプロセスなのかを知ることは困難です。しかし、UNIX ドメインなら、同じコンピュータに存在することが判っているので、相手側プロセスが何者なのか、以上のように調べれば判ります。

しかし、あくまで「調べれば判る」というレベルです。例えば、目的のプロセスが2つあって、それらがつながっているかどうか、どうすれば調べられるでしょうか? @/tmp/ibus/dbus-PSq26h33 を使ってつながっているプロセスはたくさんあることが想定されます。

luna:~ % ss -anp |grep ^u_str |grep '@/tmp/ibus/dbus-PSq26h33'  | wc -l
10
luna:~ % 

10個ですね。10個なら一行ずつ調べていけば、まぁなんとかなります。

でも、面倒ですよね?

そこで、ツールを作ってみました。

#!/usr/bin/env ruby

data = []

`ss -anp`.split("\n").each do |line|
  fields = line.split(/\s+/, 9)
  next if fields[0] != 'u_str'
  next if fields[1] != 'ESTAB'
  l_port = fields[5]
  r_port = fields[7]
  dat = {
    socket: fields[4],
    l_port: fields[5],
    r_port: fields[7],
    prog: fields[8],
  }
  data << dat
end

r_port_info = {}
data.each do |dat|
  raise "r_port duplicated: #{dat}." if r_port_info[dat[:l_port]]
  r_port_info[dat[:l_port]] = dat
end

data.each do |dat|
  l_dat = dat
  r_dat = r_port_info[dat[:r_port]]
  next unless r_dat

  next unless l_dat[:prog]
  next unless r_dat[:prog]

  puts "#{l_dat[:l_port]} #{l_dat[:r_port]} #{l_dat[:prog]} #{r_dat[:prog]}"
end

ここまでに説明した ss コマンドを使った調べ方を使って、愚直に接続元と接続先をペアにして列挙するだけのツールです。 仮に uconn という名前にしましょう。

このツールを使えば、例えば ibus-daemon と emacs がつながっているかは、以下のように簡単に確認できます。

luna:~ % uconn |grep ibus-daemon | grep emacs
199631 203253 users:(("emacs",pid=607815,fd=14)) users:(("ibus-daemon",pid=607643,fd=14))
203253 199631 users:(("ibus-daemon",pid=607643,fd=14)) users:(("emacs",pid=607815,fd=14))
luna:~ % 

ibus-daemon と emacs がちゃんとつながっていることが判りますね。

まとめ

UNIX ドメインソケットのおさらいと、ss コマンドの出力の見方、そしてその出力を整形して見やすく出力するツールを紹介しました。日常的に Linux の管理をしていると、時々このツールを使う場面があります。

ただ、途中で少し説明したとおり、ss コマンドはそこそこ最近生まれたコマンドなので、古めの OS だと存在しないくて、このツールが使えません。弊社のサーバも、ss コマンドが存在しないくらいには古い OS なので、このツールは使えません。残念です。

ではまた!