who コマンド

UNIX 系のシステムでの基本コマンド who の模造品です。 ログインしている状況を示す utmp ファイルの情報をとりだしています。

■ 使い方

デフォルトの who コマンドの出力と、 オプションとして「-i」または「-u」による IDLE TIME の表示をサポートしています。

オプション意味
-u または -iIDLE 時間の表示
$ who.rb   
tetsu    tty1     May 10 20:35 
tetsu    ttyp0    May 10 20:35 (:0.0)
tetsu    ttyp1    May 10 20:40 (:0.0)
tetsu    ttyp2    May 10 20:40 (:0.0)
$ who.rb -u
tetsu    tty1     May 10 20:35 09:04 
tetsu    ttyp0    May 10 20:35   .   (:0.0)
tetsu    ttyp1    May 10 20:40 00:20 (:0.0)
tetsu    ttyp2    May 10 20:40 02:45 (:0.0)

■ ソースコード

Web ブラウザでの表示上は Ruby のスクリプトですが、HTML の特殊文字をエスケープしています。 テキストとしてセーブしてご利用ください。

#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/toolbox/who.rb
# Created: May 11,1999 Tuesday 04:21:55
# Author: tetsu(WATANABE Tetsuya)
# $Id: who.rb,v 1.4 2003/05/08 03:09:53 tetsu Exp $
# usage:

def idle_time(dev)
  t = Time.now - File.atime('/dev/' + dev)
  d = 0
  if t <= 59
    '  .   '
  else
    if t > 24 * 3600
      d = t / (24 * 3600)
      t %= 24 * 3600
    end
    h = t / 3600
    m = t % 3600 / 60
    if d == 0
      format('%02d:%02d ', h, m)
    else
      format('%d+%02d:%02d ', d, h, m)
    end
  end
end

def usage
  STDERR.print("usage: #{$0} [-u] [-i]\n")
  exit 1
end

TYPE_IN = 0
LINE_IN = 3
NAME_IN = 5
HOST_IN = 6
TIME_IN = 10

UTMP = '/var/run/utmp'
UTMP_FMT = 'ssiA32A4A32A256ssllliiiiA20'
UTMP_LEN = 384

opt_u = false

while ARGV[0] =~ /^-/
  case ARGV.shift
  when /u/, /i/
    opt_u = true
  else
    usage
  end
end

utmp = File.open(UTMP)

while utmp_line = utmp.read(UTMP_LEN)
  arr = utmp_line.unpack(UTMP_FMT).values_at(TYPE_IN, NAME_IN, LINE_IN, HOST_IN, TIME_IN)
  key = arr[2]

  case arr.shift
  when 7                        # USER_PROCESS
    printf("%-8s %-8s %s %s\n",
           arr[0],
           arr[1],
           Time.at(arr[3]).strftime('%b %d %H:%M'),
           if opt_u
             idle_time(arr[1])
           else ''
           end + if arr[2] == '' then '' else '(' + arr[2] + ')' end
           )
  end
end

utmp.close

exit

■ 解説

どこかと同じような内容なのですが...

who コマンドは、データファイルになる /var/run/utmp の形式に依存しています。 この形式の定義は /usr/include/utmp.h にあります。 この定義をもとにデータをとりだすための unpack の定義を決めます。 これを手作業で実施すると大変なので、pstruct というコマンドを利用します。 これは、Perl システムが提供するコマンド(Perl スクリプト)ですが、utmp ファイルの「utmp 構造体」の形式を確認しやすくなります。 Ruby スクリプト中で使用している、構造体の大きさ(UTMP_LEN)や unpack のフォーマット(UTMP_FMT)などを、この情報をもとに決めています。 この pstruct というツールは、便利ですね。

ファイルフォーマットの情報がわかり、データを扱えるようになったらログインしている状況を who コマンドと同じように扱えるようにします。 「ユーザ名」、「ターミナルデバイス」、「ログイン時間」、「どこから接続しているか」などの情報です。 今回は、オプションとして IDLE TIME (-i, -u) をサポートしています。 この情報を得るためにターミナルデバイスのアクセス時間と現在時間の比較を行っています。

同じような内容を扱っている 「 last コマンドを作る Linux glibc version 」 も参考にしてください。


渡辺哲也(WATANABE Tetsuya): Tetsuya.WATANABE atmark nifty.com