# 機能設計書 61-devdデバイスイベント

## 概要

本ドキュメントは、FreeBSDのdevdデバイスイベント通知デーモンの機能設計を記述する。devdは、カーネルから送出されるデバイスの接続・切断・状態変化等のイベントを監視し、設定ファイルに基づいてアクションを自動実行するデーモンである。

### 本機能の処理概要

**業務上の目的・背景**：現代のコンピュータシステムではUSBデバイスやネットワークカードなどのホットプラグが日常的に発生する。管理者が手動で対応するのではなく、デバイスの接続・切断イベントに応じて自動的にドライバロード、ネットワーク設定、マウント処理などを実行する仕組みが必要である。devdはこの自動化を担うデーモンである。

**機能の利用シーン**：USBデバイスの挿抜時の自動マウント/アンマウント、ネットワークインタフェースのリンクアップ/ダウン検出、ZFSプール状態変化通知、電源イベント（ACアダプタ接続/切断）への対応、Bluetoothデバイスの検出など、あらゆるハードウェアイベントに対する自動応答処理に利用される。

**主要な処理内容**：
1. `/dev/devctl` からカーネルデバイスイベントを読み取る
2. イベントを attach(+)、detach(-)、nomatch(?)、notify(!) の4種類に分類する
3. 設定ファイル（`/etc/devd.conf` および `/etc/devd/*.conf`）をパースしてルールを構築する
4. イベント内容と設定ルールの正規表現マッチングを行う
5. マッチしたルールに対応するアクション（シェルコマンド）を実行する
6. UNIXドメインソケット（`/var/run/devd.pipe`）を通じて外部クライアントにイベントを通知する

**関連システム・外部連携**：カーネルのdevctlサブシステム（`/dev/devctl`）からイベントを受信する。devctl(4)はデバイスドライバやGEOM、ZFSなどのカーネルサブシステムからイベントを集約する。外部クライアントは `/var/run/devd.pipe`（ストリーム）または `/var/run/devd.seqpacket.pipe`（シーケンスパケット）経由でイベントを受信可能である。

**権限による制御**：devdはroot権限で動作するデーモンであり、実行するアクションもroot権限で実行される。クライアントソケットへの接続には特別な権限は不要だが、アクションの実行はdevdプロセス自身が行う。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | ファームウェアインストール画面 | 主機能 | fwgetによるハードウェアファームウェアパッケージ検出・pkg経由インストール |

## 機能種別

イベント駆動型デーモン / デバイスイベント処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| -c configfile | string | No | 設定ファイルパス（デフォルト: /etc/devd.conf） | ファイルが存在すること |
| -d | flag | No | デバッグモード（デーモン化しない） | なし |
| -f | flag | No | フォアグラウンドで起動 | なし |
| -l connlimit | int | No | 同時接続クライアント数上限 | 正の整数 |
| -n | flag | No | ドライランモード | なし |
| -q | flag | No | 静粛モード | なし |

### 入力データソース

- `/dev/devctl`: カーネルデバイスイベントソース（devctl_maxbuf = 8192バイト）
- `/etc/devd.conf`: メイン設定ファイル
- `/etc/devd/*.conf`: 追加設定ファイル群（apple.conf, bluetooth.conf, hyperv.conf, zfs.conf等）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| イベント通知 | string | UNIXドメインソケット経由のイベント文字列 |
| アクション実行結果 | int | シェルコマンドの終了ステータス |
| syslogメッセージ | string | イベント処理のログ出力 |

### 出力先

- `/var/run/devd.pipe`: ストリーム型UNIXドメインソケット（外部クライアント通知用）
- `/var/run/devd.seqpacket.pipe`: シーケンスパケット型UNIXドメインソケット
- syslog: イベント処理ログ
- シェル実行: マッチしたアクションコマンドの実行

## 処理フロー

### 処理シーケンス

```
1. 起動・初期化
   └─ コマンドライン引数パース、デーモン化、PIDファイル作成
2. 設定ファイルパース
   └─ /etc/devd.conf および /etc/devd/*.conf をパースし、
      attach/detach/nomatch/notify の各イベントリストを構築
3. ソケット作成
   └─ STREAM型とSEQPACKET型のUNIXドメインソケットを作成
4. イベントループ開始
   └─ /dev/devctl と接続クライアントをpoll()で監視
5. イベント受信
   └─ devctlから文字列イベントを読み取り、先頭文字で種別を判定
6. 変数展開・マッチング
   └─ イベント内の変数をパースし、設定ルールの正規表現と照合
7. アクション実行
   └─ マッチしたルールのコマンドをfork/exec（/bin/sh -c）で実行
8. クライアント通知
   └─ 接続中のクライアントソケットにイベント文字列を送信
```

### フローチャート

```mermaid
flowchart TD
    A[devd起動] --> B[設定ファイルパース]
    B --> C[UNIXドメインソケット作成]
    C --> D[devctl FDオープン]
    D --> E{poll: イベント待機}
    E -->|devctlイベント| F[イベント文字列読み取り]
    E -->|新規クライアント接続| G[クライアント登録]
    F --> H{イベント種別判定}
    H -->|+: attach| I[attach_listとマッチング]
    H -->|-: detach| J[detach_listとマッチング]
    H -->|?: nomatch| K[nomatch_listとマッチング]
    H -->|!: notify| L[notify_listとマッチング]
    I --> M{マッチ?}
    J --> M
    K --> M
    L --> M
    M -->|Yes| N[アクションコマンド実行]
    M -->|No| O[クライアントへ通知]
    N --> O
    O --> E
    G --> E
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-61-01 | イベント種別分類 | イベント先頭文字により +（attach）、-（detach）、?（nomatch）、!（notify）に分類 | 全イベント |
| BR-61-02 | 優先度順実行 | 同一種別のルールは優先度（priority）の降順で評価 | 複数ルールがマッチする場合 |
| BR-61-03 | 正規表現マッチ | match文のパターンは拡張正規表現（REG_EXTENDED）で大文字小文字無視（REG_ICASE） | matchキーワード使用時 |
| BR-61-04 | 否定マッチ | パターン先頭が`!`の場合は否定マッチ（マッチしない場合にtrue） | パターン先頭が! |
| BR-61-05 | クライアントバッファ | クライアントソケットの送信バッファは262144バイト（256KB） | クライアント接続時 |

### 計算ロジック

特になし。

## データベース操作仕様

### 操作別データベース影響一覧

本機能はデータベースを使用しない。設定ファイルとカーネルイベントインタフェースを使用する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EEXIST | PIDファイル競合 | devdが既に起動中 | エラーメッセージ表示後exit |
| EIO | devctlオープン失敗 | /dev/devctlが存在しない | syslogに記録、再試行 |
| EINTR | シグナル割り込み | SIGHUPによる再設定 | 設定ファイル再読み込み |
| - | 設定パースエラー | devd.confの文法エラー | syslogにエラー出力、該当行をスキップ |

### リトライ仕様

devctlデバイスの読み取りエラー時はイベントループを継続する。SIGHUPシグナル受信時は設定ファイルを再パースする。

## トランザクション仕様

トランザクション管理は不要。各イベントは独立して処理される。

## パフォーマンス要件

- devctlキューサイズはsysctl `hw.bus.devctl_queue` で設定可能（デフォルト1000）
- クライアントソケットバッファ256KBにより、約450台のドライブによるZFSプール作成時のイベントバーストに対応

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

- devdはroot権限で動作し、アクションコマンドもroot権限で実行されるため、設定ファイルの改竄は深刻なセキュリティリスクとなる
- 設定ファイルのパーミッションはrootのみ書き込み可能であることを前提とする
- アクションコマンド実行時はシェルクォートによるインジェクション対策を実施（`shell_quote`メソッド）
- クライアントソケットは `/var/run/devd.pipe` に作成され、システム全体からアクセス可能

## 備考

- vm_guest変数により仮想マシンゲスト環境を検出し、$vm-guest変数として設定ファイル内で参照可能
- devdは設定ファイルの変更を自動検出しないため、変更後はSIGHUPまたはサービス再起動が必要

---

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

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

### 推奨読解順序

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

devdの設定とイベント処理の中核となるクラス群を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | devd.h | `sbin/devd/devd.h` | C互換のインタフェース定義。PATH_DEVCTL、DEVCTL_MAXBUF定数、パーサ関数宣言 |
| 1-2 | devd.hh | `sbin/devd/devd.hh` | C++クラス定義。var_list（変数管理）、eps/match/media/action（イベント処理単位）、event_proc（イベントプロシージャ）、config（全体設定）の各クラス |

**読解のコツ**: devdはC++で実装されているが、パーサ部分（parse.y, token.l）はC互換であるため、devd.hがC互換ヘッダとして分離されている。configクラスの4つのリスト（_attach_list, _detach_list, _nomatch_list, _notify_list）がイベント種別ごとの処理ルール集合である。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | devd.cc | `sbin/devd/devd.cc` | main()関数とイベントループ |

**主要処理フロー**:
1. **102-105行目**: 定数定義（STREAMPIPE, SEQPACKETPIPE, CF, SYSCTL）
2. **140-143行目**: イベント種別文字の定義（notify='!', nomatch='?', attach='+', detach='-'）
3. **177-213行目**: event_procクラスの実装（matches/runメソッド）
4. **226-278行目**: my_system()関数 - fork/execによるコマンド実行
5. **280-287行目**: action::do_action() - 変数展開後のコマンド実行
6. **289-295行目**: match::match() - 正規表現コンパイル（REG_EXTENDED|REG_NOSUB|REG_ICASE）
7. **517-529行目**: config::parse() - 設定ファイルパースとリストソート

#### Step 3: 設定ファイルパーサを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | parse.y | `sbin/devd/parse.y` | yacc文法定義。attach/detach/nomatch/notifyブロックのパース |
| 3-2 | token.l | `sbin/devd/token.l` | lex字句解析定義。キーワードと変数の認識 |

#### Step 4: 設定ファイルの構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | devd.conf | `sbin/devd/devd.conf` | デフォルト設定。options、attach/detach/nomatchブロックの実例 |
| 4-2 | zfs.conf | `sbin/devd/zfs.conf` | ZFS状態変化通知の設定例 |
| 4-3 | bluetooth.conf | `sbin/devd/bluetooth.conf` | Bluetooth関連イベント設定例 |

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

```
main() [devd.cc]
    |
    +-- cfg.parse() [devd.cc:517-529]
    |       +-- parse_one_file() -> yyparse() [parse.y]
    |       +-- parse_files_in_dir() -> parse_one_file()
    |       +-- sort_vector() [attach/detach/nomatch/notify各リスト]
    |
    +-- event_loop() [devd.cc]
            |
            +-- poll() [devctlFD + クライアントソケット]
            |
            +-- cfg.set_vars() [イベント変数のパース]
            |
            +-- cfg.find_and_execute() [イベント種別に応じたリスト走査]
                    |
                    +-- event_proc::matches() [正規表現マッチング]
                    |       +-- match::do_match() / media::do_match()
                    |
                    +-- event_proc::run() [アクション実行]
                            +-- action::do_action()
                                    +-- my_system() [fork/exec sh -c]
```

### データフロー図

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

/dev/devctl ──────────▶ event_loop() ─────────────▶ /var/run/devd.pipe
(カーネルイベント)         |                            (クライアント通知)
                          |
/etc/devd.conf ────▶ cfg.parse() ──▶ ルールリスト
/etc/devd/*.conf ──┘                    |
                                         v
                                  find_and_execute()
                                         |
                                         v
                                  my_system() ────────▶ シェルコマンド実行
                                                        syslog出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| devd.cc | `sbin/devd/devd.cc` | ソース | メインデーモン実装（C++） |
| devd.hh | `sbin/devd/devd.hh` | ヘッダ | C++クラス定義 |
| devd.h | `sbin/devd/devd.h` | ヘッダ | C互換インタフェース定義 |
| parse.y | `sbin/devd/parse.y` | ソース | yacc文法定義 |
| token.l | `sbin/devd/token.l` | ソース | lex字句解析定義 |
| devd.conf | `sbin/devd/devd.conf` | 設定 | デフォルト設定ファイル |
| zfs.conf | `sbin/devd/zfs.conf` | 設定 | ZFSイベント設定 |
| bluetooth.conf | `sbin/devd/bluetooth.conf` | 設定 | Bluetoothイベント設定 |
| hyperv.conf | `sbin/devd/hyperv.conf` | 設定 | Hyper-Vイベント設定 |
| devd.8 | `sbin/devd/devd.8` | マニュアル | devdマニュアルページ |
| devd.conf.5 | `sbin/devd/devd.conf.5` | マニュアル | 設定ファイルマニュアル |
