# 帳票設計書 30-last - ユーザーログイン履歴レポート

## 概要

本ドキュメントは、FreeBSDのlastコマンドが出力するユーザーログイン履歴レポートの帳票設計書である。lastはutx.logファイルを読み取り、ユーザーのログイン/ログアウト履歴を時系列で表示する。

### 本帳票の処理概要

lastコマンドは、utmpxデータベース（utx.log）を読み取り、ユーザーのログインセッション情報を逆時系列でレポートする。libxo(3)を使用しており、テキスト、JSON、XML形式での出力に対応する。Capsicumサンドボックス内で動作する。

**業務上の目的・背景**：システムのセキュリティ監査やユーザー活動の追跡において、誰がいつログインしたかの履歴情報は重要である。不正アクセスの検出、システム障害時のログイン状況確認、利用状況のレポート作成に使用される。

**帳票の利用シーン**：セキュリティ監査でのログイン履歴確認、不正アクセス調査、システムリブート/シャットダウン履歴の確認、特定ユーザーのログイン時間帯把握、TTY使用状況の確認などで使用される。

**主要な出力内容**：
1. ユーザー名: ログインしたユーザー名
2. TTY: 使用されたターミナル
3. ホスト名: リモートログイン元のホスト名
4. ログイン時刻: セッション開始時刻
5. ログアウト時刻/理由: セッション終了時刻または"still logged in"/"crash"/"shutdown"
6. セッション長: ログイン期間

**帳票の出力タイミング**：コマンドラインからlastコマンドを実行した時点で即座に出力される。

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

## 帳票種別

一覧表（テキスト/JSON/XML形式）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | コマンドライン | ターミナル | `last [オプション] [user ...]` 実行 |

## 出力形式

### 基本仕様

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

## 帳票レイアウト

### レイアウト概要

```
┌──────────────────────────────────────────────────────────┐
│ user     tty      host                login  - logout    │
├──────────────────────────────────────────────────────────┤
│ admin    pts/0    192.168.1.100       Mon Jan 15 09:30   │
│                                       - 17:30 (08:00)    │
│ root     tty0                         Mon Jan 15 08:00   │
│                                       - still logged in  │
│ shutdown time                         Sun Jan 14 23:00   │
│ boot time                             Sun Jan 14 22:00   │
├──────────────────────────────────────────────────────────┤
│ utx.log begins Sun Jan 14 22:00:00                      │
└──────────────────────────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| N/A | なし | ヘッダー行は出力されない | - | - |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | user | ユーザー名/イベント名 | utmpx.ut_user | %-10s (USER_PROCESS) | 10文字 |
| 2 | tty | ターミナル名 | utmpx.ut_line | %-8s | 8文字 |
| 3 | from | リモートホスト名 | utmpx.ut_host | %-22.22s | 22文字 |
| 4 | login-time | ログイン時刻 | utmpx.ut_tv | strftime形式 | 可変 |
| 5 | logout-time | ログアウト時刻 | idtab.logout | HH:MM or "still logged in" | 可変 |
| 6 | logout-reason | ログアウト理由 | crmsg | "crash"/"shutdown" | 可変 |
| 7 | session-length | セッション長 | 計算値 | HH:MM:SS or Nd+HH:MM:SS | 可変 |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | utxdb行 | utx.logの開始時刻 | 最古エントリのタイムスタンプ | "utx.log begins %s" |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| user引数 | 特定ユーザーのみ表示 | No |
| -h host | 特定ホストからのログインのみ | No |
| -t tty | 特定TTYのみ | No |
| -n maxrec | 最大表示レコード数 | No |
| -f file | 代替utx.logファイル指定 | No |
| -d datetime | スナップショット時点指定 | No |
| -s | セッション長を秒で表示 | No |
| -w | 秒まで表示（幅広フォーマット） | No |
| -y | 年を表示 | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | タイムスタンプ | 降順（新しい順） |

### 改ページ条件

テキスト出力のため改ページなし。

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

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| utx.log（utmpxデータベース） | ログイン/ログアウトイベント | setutxdb()で指定 |

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

#### utmpxレコード

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| ut_user | user | getutxent()で順次取得 | ユーザー名 |
| ut_line | tty | getutxent()で順次取得 | ターミナル名 |
| ut_host | from | getutxent()で順次取得 | リモートホスト |
| ut_tv | login-time | getutxent()で順次取得 | タイムスタンプ |
| ut_type | イベント種別 | getutxent()で順次取得 | USER_PROCESS, BOOT_TIME等 |
| ut_id | セッション識別子 | getutxent()で順次取得 | ログアウト対応付け用 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| session-length | logout_time - login_time | 秒単位 | 1日以上の場合"Nd+HH:MM:SS"形式 |
| logout判定 | idtab.logoutの符号 | N/A | 負値→crash/shutdown、正値→正常ログアウト |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[コマンド実行] --> B[xo_parse_args - libxo初期化]
    B --> C[getopt - オプション解析]
    C --> D[Capsicum初期化]
    D --> E[setutxdb - utx.logオープン]
    E --> F[wtmp関数呼び出し]
    F --> G[getutxent - 全エントリ読み込み]
    G --> H[逆順でdoentry処理]
    H --> I{イベント種別}
    I -->|BOOT/SHUTDOWN| J[全セッション終了処理]
    I -->|USER_PROCESS| K[ログインエントリ表示]
    I -->|DEAD_PROCESS| L[ログアウト時刻記録]
    J --> M[printentry - 表示]
    K --> M
    M --> N[次のエントリ]
    N --> H
    H -->|全件処理完了| O[フッター出力]
    O --> P[xo_finish]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| utx.logオープン失敗 | ファイル不存在/権限不足 | xo_err()出力 | ファイルの存在と権限を確認 |
| 無効な日時形式 | -dオプションの書式エラー | "out of range or illegal time specification" | 正しい書式で指定 |
| メモリ不足 | realloc失敗 | xo_err()出力 | メモリ解放 |
| -sと-w同時指定 | 矛盾オプション | usage()表示 | どちらか一方を指定 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数百〜数万ログインエントリ |
| 目標出力時間 | 数秒以内 |
| 同時出力数上限 | 制限なし |

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

- Capsicumサンドボックス内で動作（caph_enter()使用）
- caph_limit_stdio()でstdio権限を制限
- caph_cache_catpages/caph_cache_tzdataで必要なデータをキャッシュ
- utx.logにはユーザーのログイン元ホスト情報が含まれるため、プライバシーに配慮が必要
- -fオプションで任意のファイルを指定可能だが、Capsicumにより制限される

## 備考

- lastはBSD由来の伝統的なコマンド
- libxo(3)対応でJSON/XML出力が可能
- utmpx APIを使用（旧utmpは非推奨）
- スナップショットモード（-d）では指定時点でログイン中のユーザーのみ表示
- "reboot"引数を指定するとリブート/シャットダウンイベントのみ表示
- 互換性のため"last p5"（数字引数）でのレコード数指定に対応

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | last.c | `usr.bin/last/last.c` | ARG構造体（行59-67）: フィルタ引数のリンクリスト。type（REBOOT_TYPE, HOST_TYPE, TTY_TYPE, USER_TYPE）とnameを保持 |
| 1-2 | last.c | `usr.bin/last/last.c` | struct idtab（行72-76）: セッション追跡構造体。logout時刻とut_idを保持。SLISTで管理 |
| 1-3 | last.c | `usr.bin/last/last.c` | グローバル変数群: crmsg（行78）、currentout（行79）、maxrec（行80）、file（行81）、sflag/width/yflag（行82-84）、snaptime/snapfound（行86-90） |

**読解のコツ**: idtabのlogoutフィールドの符号に注意。正値→正常ログアウト時刻、負値→crash/shutdownによるログアウト（-bp->ut_tv.tv_sec形式）。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | last.c | `usr.bin/last/last.c` | main()関数（行111-212）。xo_parse_args()→getopt()→Capsicum初期化→setutxdb()→wtmp() |

**主要処理フロー**:
1. **行117-118**: setlocale()とnl_langinfo()でロケール設定
2. **行120-122**: xo_parse_args()でlibxo初期化
3. **行126-178**: getopt()によるオプション解析（-d, -f, -h, -n, -s, -t, -w, -y）
4. **行180-183**: Capsicum初期化（caph_limit_stdio, caph_cache_*）
5. **行187-188**: setutxdb(UTXDB_LOG, file)でutx.logオープン
6. **行190-191**: caph_enter()でサンドボックス突入
7. **行195-207**: 引数解析（ユーザー名、"reboot"、TTY変換）
8. **行208**: wtmp()呼び出し

#### Step 3: メイン処理ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | last.c | `usr.bin/last/last.c` | wtmp()関数（行218-258）: getutxent()で全エントリを読み込み、逆順でdoentry()を呼び出す |
| 3-2 | last.c | `usr.bin/last/last.c` | doentry()関数（行264-332）: イベント種別ごとの処理。BOOT_TIME/SHUTDOWN_TIME→全セッション終了、USER_PROCESS→ログイン表示、DEAD_PROCESS→ログアウト記録 |
| 3-3 | last.c | `usr.bin/last/last.c` | printentry()関数（行341-411）: 個別エントリの出力。ユーザー名、TTY、ホスト、ログイン時刻、ログアウト時刻/理由、セッション長をxo_emit()で出力 |
| 3-4 | last.c | `usr.bin/last/last.c` | want()関数（行417-448）: フィルタ関数。arglistの各条件とエントリを照合 |

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

```
main() [last.c 行111]
    │
    ├─ xo_parse_args() - libxo初期化 [行120]
    ├─ getopt() - オプション解析 [行126]
    ├─ caph_limit_stdio() - Capsicum [行180]
    ├─ setutxdb(UTXDB_LOG) - utx.logオープン [行187]
    ├─ caph_enter() - サンドボックス [行190]
    ├─ addarg() - フィルタ条件追加 [行199-205]
    │
    └─ wtmp() [行218]
           │
           ├─ getutxent() - 全エントリ読み込み [行234]
           ├─ xo_open_container/list
           │
           └─ doentry() (逆順ループ) [行264]
                  │
                  ├─ [BOOT/SHUTDOWN_TIME]
                  │      ├─ idlist全クリア
                  │      ├─ currentout/crmsg設定
                  │      └─ printentry() [行341]
                  │
                  ├─ [USER_PROCESS]
                  │      ├─ idtab検索/作成
                  │      ├─ want() - フィルタチェック [行417]
                  │      └─ printentry() [行341]
                  │             ├─ xo_emit(user, tty, from)
                  │             ├─ xo_emit(login-time)
                  │             ├─ xo_emit(logout-time/reason)
                  │             └─ xo_emit(session-length)
                  │
                  └─ [DEAD_PROCESS]
                         └─ idtab.logout更新
```

### データフロー図

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

utx.log         ───▶ getutxent()          ───▶ utmpxレコード配列
(utmpxDB)             │
                      ▼
                 逆順でdoentry()
                      │
                      ├─ BOOT/SHUTDOWN ───▶ セッション終了処理
                      ├─ USER_PROCESS  ───▶ want()フィルタ
                      │                      │
                      │                      ▼
                      │                 printentry()
                      │                      │
                      │                      ├─ xo_emit(user)
                      │                      ├─ xo_emit(tty)
                      │                      ├─ xo_emit(from)
                      │                      ├─ xo_emit(login-time)
                      │                      ├─ xo_emit(logout)
                      │                      └─ xo_emit(session-length)
                      │
                      └─ DEAD_PROCESS  ───▶ idtab更新
                                               │
                                               ▼
                                          標準出力
                                          (text/json/xml)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| last.c | `usr.bin/last/last.c` | ソース | 全ロジック（591行の単一ファイル） |
| Makefile | `usr.bin/last/Makefile` | ビルド | ビルド設定 |
