# 帳票設計書 33-w

## 概要

本ドキュメントは、FreeBSDのwコマンドが生成するシステムステータスレポートの設計を記述する。wはログイン中のユーザー一覧と各ユーザーが実行中のプロセス情報をレポートするCLIユーティリティである。uptimeコマンドとしても動作する。

### 本帳票の処理概要

wコマンドは、utmpxデータベースからログインユーザー情報を取得し、kvm(3)インターフェースを通じてカーネルのプロセステーブルから各ユーザーが実行中の「最も興味深い」プロセスを特定し、システムの稼働状況と共にレポート出力する。libxo(3)を使用してテキスト/JSON/XML形式の出力をサポートする。

**業務上の目的・背景**：マルチユーザーシステムにおいて、誰がログインしており、何を実行しているかを把握することはシステム管理の基本である。wコマンドはこの情報を一覧形式で提供し、システムの利用状況監視やリソース管理に活用される。

**帳票の利用シーン**：ログイン中ユーザーの活動状況確認、アイドル状態のセッション検出、システム負荷の概況確認（uptime/ロードアベレージ）、不正ログインの検知に利用される。

**主要な出力内容**：
1. ヘッダー行：現在時刻、稼働時間、ログインユーザー数、ロードアベレージ
2. 明細行：ユーザー名、端末名、接続元、ログイン時刻、アイドル時間、実行中コマンド

**帳票の出力タイミング**：ユーザーがコマンドラインからwまたはuptimeコマンドを実行した際に即時出力される。

**帳票の利用者**：システム管理者、セキュリティ担当者

## 帳票種別

一覧表 / ヘッダー付きサマリーレポート

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | CLIターミナル | N/A | `w [options] [user ...]` コマンド実行 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト / JSON / XML（libxo経由） |
| 用紙サイズ | N/A（ターミナル出力） |
| 向き | N/A |
| ファイル名 | N/A（標準出力） |
| 出力方法 | 標準出力（stdout）via libxo |
| 文字コード | ロケール依存 |

### PDF固有設定

該当なし

### Excel固有設定

該当なし

## 帳票レイアウト

### レイアウト概要

```
┌──────────────────────────────────────────────────────────────────┐
│  ヘッダー行: 時刻 up 稼働時間, N users, load averages: X, Y, Z    │
├──────────────────────────────────────────────────────────────────┤
│  カラムヘッダー: USER  TTY  FROM  LOGIN@  IDLE  WHAT             │
├──────────────────────────────────────────────────────────────────┤
│  明細行1: alice  pts/0  192.168.1.10  10:30AM  1:23  vim foo.c  │
│  明細行2: bob    pts/1  :0            09:15AM    -   bash        │
│  ...                                                            │
└──────────────────────────────────────────────────────────────────┘
```

### ヘッダー部（pr_header関数、行473-576）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | time-of-day | 現在時刻 | localtime(now) | %l:%M%p（AM/PM）or %k:%M |
| 2 | uptime-human | システム稼働時間 | clock_gettime(CLOCK_UPTIME) | "X days, HH:MM" |
| 3 | users | ログインユーザー数 | utmpxエントリーカウント | "%d users" |
| 4 | load-average-1 | 1分ロードアベレージ | getloadavg() | "%.2f" |
| 5 | load-average-5 | 5分ロードアベレージ | getloadavg() | "%.2f" |
| 6 | load-average-15 | 15分ロードアベレージ | getloadavg() | "%.2f" |

### カラムヘッダー部

| 項目名 | 幅 | 定義箇所 |
|-------|---|---------|
| USER | 10文字（W_DISPUSERSIZE） | 行113 |
| TTY | 8文字（W_DISPLINESIZE） | 行114 |
| FROM | 最大40文字（W_MAXHOSTSIZE） | 行115 |
| LOGIN@ IDLE | 14文字 | 行313 |
| WHAT | 残り幅 | 行314 |

### 明細部（行417-461）

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | user | ユーザー名 | utmp.ut_user | "%-10.10s" | 10文字 |
| 2 | tty | 端末名 | utmp.ut_line | "%-8.8s" | 8文字 |
| 3 | from | 接続元ホスト/IP | utmp.ut_host + DNS解決 | "%-*.*s" | 動的（最大40文字） |
| 4 | login-time | ログイン時刻 | utmp.ut_tv.tv_sec + pr_attime() | 7文字 | 7文字 |
| 5 | idle | アイドル時間 | now - tty_atime + pr_idle() | 6文字 | 6文字 |
| 6 | command | 実行中コマンド | kvm_getargv() + fmt_argv() | 残り幅 | 動的 |

### フッター部

該当なし

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| ユーザー名 | 特定ユーザーのみ表示 | No |
| utmpx USER_PROCESS | USER_PROCESSタイプのエントリーのみ対象 | Yes（暗黙） |
| ttystat成功 | ttyデバイスがstat可能なエントリーのみ対象 | Yes（暗黙） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | utmpx登録順（デフォルト）| 登録順 |
| 2 | アイドル時間（-iオプション時）| 昇順 |

### 改ページ条件

改ページなし

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| utmpxデータベース | ログインユーザー情報取得 | setutxent()/getutxent() |
| カーネルプロセステーブル | 各端末のフォアグラウンドプロセス取得 | kvm_getprocs(KERN_PROC_ALL) |
| DNSリゾルバ | IPアドレス→ホスト名解決 | realhostname_sa() |

### テーブル別参照項目詳細

#### utmpx（ログイン情報）

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| ut_user | USER | ut_type == USER_PROCESS | 最大32文字 |
| ut_line | TTY | ut_type == USER_PROCESS | "tty"/"cua"プレフィクス除去 |
| ut_host | FROM | ut_type == USER_PROCESS | DNS逆引きまたはIP表示 |
| ut_tv.tv_sec | LOGIN@ | ut_type == USER_PROCESS | ログイン時刻 |

#### kinfo_proc（プロセス情報）

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| ki_tdev | 端末マッチング | KERN_PROC_ALL | entry.tdevとの比較 |
| ki_pgid/ki_tpgid | フォアグラウンド判定 | ki_pgid == ki_tpgid | |
| ki_comm | WHAT | proc_compare()で最も興味深いプロセスを選択 | |
| ki_stat | ステータス | SIDL, SZOMB以外 | ゾンビ/アイドル除外 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| idle時間 | now - max(tty_atime, login_time) | 秒単位 | 負の場合は0に補正（行251-252） |
| fromwidth | max(全エントリーのfromフィールド長, sizeof("FROM")) | N/A | 最大W_MAXHOSTSIZE(40)に制限 |
| argwidth | ttywidth - WUSED | N/A | 最小4、JSON/XML時はARG_MAX |
| uptime分 | (tp.tv_sec + 30) / 60 | 60秒以上の場合四捨五入 | 行514-522 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[コマンド実行] --> B[xo_parse_args/setlocale]
    B --> C[basename判定: w or uptime?]
    C --> D[getoptによるオプション解析]
    D --> E[kvm_openfiles]
    E --> F[setutxent / getutxentループ]
    F --> G{USER_PROCESS?}
    G -->|No| F
    G -->|Yes| H[ttystat確認]
    H --> I[nusersカウント]
    I --> J{wcmd == 0?}
    J -->|Yes（uptime）| K[pr_header出力のみ]
    J -->|No（w）| L[entryリスト構築]
    L --> M[ホスト名解決]
    M --> F
    F -->|完了| N[endutxent]
    N --> O[pr_header出力]
    O --> P[カラムヘッダー出力]
    P --> Q[kvm_getprocsで全プロセス取得]
    Q --> R[各プロセスを端末にマッチング]
    R --> S[proc_compareで最も興味深いプロセスを選択]
    S --> T{-iオプション?}
    T -->|Yes| U[アイドル時間でソート]
    T -->|No| V[各エントリーの明細行出力]
    U --> V
    V --> W[xo_finish]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| kvm_openfiles失敗 | カーネルメモリ読み取り不可 | "xo_errx: errbuf" | 権限確認 |
| kvm_getprocs失敗 | プロセステーブル取得失敗 | "xo_err: kvm_geterr" | 権限確認 |
| メモリ確保失敗 | calloc/strdup失敗 | "xo_errx: calloc" | メモリ不足確認 |
| fmt_argv失敗 | 引数フォーマット失敗 | "xo_err: fmt_argv" | - |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数名〜数百名のログインユーザー |
| 目標出力時間 | 1秒未満（kvm_getprocsが支配的） |
| 同時出力数上限 | 制限なし |

## セキュリティ考慮事項

- kvm(3)によるカーネルメモリアクセスには適切な権限（kmem グループ）が必要
- ユーザーの接続元IP/ホスト名が表示されるため、プライバシーに配慮が必要
- -nオプションでDNS逆引きを抑制可能（情報漏洩リスク低減）

## 備考

- uptimeとして起動された場合、ヘッダー行のみ出力しユーザー明細は出力しない
- libxo(3)によりJSON/XML形式の出力も可能（xo_emit使用）
- proc_compare()関数（proc_compare.c）はカーネルの^T処理（kern/tty.c）と同様のアルゴリズムを使用
- tty名から"tty"/"cua"プレフィクスを除去して表示する（行445-447）
- -Mオプションでコアダンプファイルからの情報取得も可能

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | w.c | `usr.bin/w/w.c` | 行97-107: struct entry定義（utmpxエントリーにtdev/idle/kp/args/fromを付加） |
| 1-2 | w.c | `usr.bin/w/w.c` | 行79-92: グローバル変数（utmp, ws, kd, now, ttywidth, fromwidth等） |

**読解のコツ**: struct entryはutmpxレコードを拡張したリンクリスト。kpフィールドが「最も興味深い」プロセスを保持する。

#### Step 2: エントリーポイント（main）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | w.c | `usr.bin/w/w.c` | 行123-471: main関数 |

**主要処理フロー**:
1. **行149-155**: uptimeモード判定（basename(argv[0]) == "uptime"）
2. **行159-186**: getoptによるオプション解析（d/h/i/M/N/n/f/l/s/u/w）
3. **行203-307**: utmpxループ - ログインユーザー収集とホスト名解決
4. **行344-345**: kvm_getprocs(KERN_PROC_ALL)で全プロセス取得
5. **行346-373**: プロセスと端末のマッチング
6. **行397-412**: -iオプション時のアイドル時間ソート（挿入ソート）
7. **行417-461**: 明細行出力ループ

#### Step 3: ヘッダー出力処理（pr_header）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | w.c | `usr.bin/w/w.c` | 行473-576: pr_header関数 |

**主要処理フロー**:
- **行486-495**: 現在時刻の表示（use_ampmによるAM/PM切り替え）
- **行499-551**: uptime計算と表示（days/hours/minutes形式）
- **行554-555**: ログインユーザー数表示
- **行560-575**: ロードアベレージ表示（1/5/15分）

#### Step 4: 時刻・アイドル表示処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | pr_time.c | `usr.bin/w/pr_time.c` | 行48-98: pr_attime関数（ログイン時刻表示） |
| 4-2 | pr_time.c | `usr.bin/w/pr_time.c` | 行105-138: pr_idle関数（アイドル時間表示） |

**主要処理フロー**:
- **行62-77**: ログイン時刻の表示形式選択（1週間超/当日以外/当日）
- **行115-136**: アイドル時間の表示形式選択（36時間超→日数/1時間超→HH:MM/0→"-"/その他→分）

#### Step 5: プロセス比較アルゴリズムを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | proc_compare.c | `usr.bin/w/proc_compare.c` | 行64-114: proc_compare関数 |

**主要処理フロー**:
- **行73-87**: 実行中プロセスの優先（CPU利用率の高いもの）
- **行91-98**: ゾンビプロセスの除外
- **行102-113**: スリープ時間の短いプロセスを優先

### プログラム呼び出し階層図

```
main() [w.c:123]
    |
    +-- xo_parse_args() [libxo]
    +-- basename() [uptime判定]
    +-- getopt() [オプション解析]
    +-- kvm_openfiles() [カーネルメモリアクセス初期化]
    +-- time() [現在時刻取得]
    |
    +-- setutxent() / getutxent() ループ [w.c:203-307]
    |       +-- ttystat() [w.c:578-588]
    |       +-- inet_pton() [IPv4/IPv6アドレス判定]
    |       +-- realhostname_sa() [DNS逆引き]
    |       +-- getaddrinfo() [正引き]
    |
    +-- pr_header() [w.c:473-576]
    |       +-- localtime() / strftime()
    |       +-- clock_gettime(CLOCK_UPTIME)
    |       +-- getloadavg()
    |
    +-- kvm_getprocs() [w.c:344]
    |
    +-- proc_compare() [proc_compare.c:64]
    |
    +-- [明細出力ループ] [w.c:417-461]
            +-- pr_attime() [pr_time.c:48]
            +-- pr_idle() [pr_time.c:105]
            +-- fmt_argv() [../../bin/ps/fmt.c]
            +-- xo_emit() [libxo]
```

### データフロー図

```
[入力]                        [処理]                          [出力]

utmpxデータベース ──────> getutxent()                     ──> ユーザー一覧
                             |                                  |
カーネルプロセステーブル ──> kvm_getprocs()                  ──> プロセス一覧
                             |                                  |
ttyデバイスファイル ──────> ttystat()                       ──> アイドル時間
                             |                                  |
DNS ──────────────────> realhostname_sa()                 ──> ホスト名
                             |                                  |
CLOCK_UPTIME ─────────> clock_gettime()                  ──> 稼働時間
/proc/loadavg ────────> getloadavg()                     ──> ロードアベレージ
                             |                                  |
                       +-- pr_header() + 明細ループ          ──> 標準出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| w.c | `usr.bin/w/w.c` | ソース | メインプログラム（main, pr_header, ttystat） |
| pr_time.c | `usr.bin/w/pr_time.c` | ソース | 時刻・アイドル時間表示（pr_attime, pr_idle） |
| proc_compare.c | `usr.bin/w/proc_compare.c` | ソース | プロセス比較アルゴリズム |
| extern.h | `usr.bin/w/extern.h` | ヘッダ | 関数プロトタイプ宣言 |
| fmt.c | `bin/ps/fmt.c` | ソース | プロセス引数フォーマット（fmt_argv） |
| Makefile | `usr.bin/w/Makefile` | 設定 | ビルド設定（uptimeリンクを含む） |
