# 機能設計書 72-shutdown / reboot

## 概要

本ドキュメントは、FreeBSDのシステム停止・再起動機能（shutdown、reboot、halt、nextboot）の機能設計について記述する。これらのコマンドは、システムの安全な停止・再起動を実行するための管理者向けユーティリティである。

### 本機能の処理概要

**業務上の目的・背景**：サーバやワークステーションの計画的な停止・再起動は、システム管理の基本操作である。メンテナンス作業、カーネルアップデート後の再起動、緊急停止など、様々な場面で安全にシステムを停止・再起動する手段が必要である。shutdownコマンドはログインユーザへの事前通知機能を持ち、reboot/haltコマンドは即座にシステムを停止・再起動する。

**機能の利用シーン**：計画停止前のユーザ通知付きシャットダウン（shutdown -h +10）、即座の再起動（reboot）、電源オフ（poweroff / shutdown -p now）、次回起動時のカーネル指定（nextboot -k kernel）、ダンプ付き再起動（reboot -d）。

**主要な処理内容**：
1. shutdown: 指定時刻まで定期的にログインユーザへ警告メッセージを送信し、時刻到来時にシステムを停止/再起動する
2. reboot: init(8)にSIGINTを送信してシステム再起動を要求する
3. halt: init(8)にSIGUSR1を送信してシステム停止を要求する
4. nextboot: 次回起動時のブート設定（カーネル、環境変数）を/boot/nextboot.confに書き込む
5. poweroff: shutdown -p nowと等価のシステム電源オフを実行する

**関連システム・外部連携**：init(8)プロセスへのシグナル送信、utmpxによるログインユーザ通知、syslog(3)によるログ記録、reboot(2)システムコールの呼び出し、ZFS環境でのzfsbootcfg連携。

**権限による制御**：shutdown、reboot、haltはすべてroot権限（euid == 0）が必要。一般ユーザが実行した場合はエラーとなる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | （画面なし） | - | コマンドラインツールであり直接的な画面UIを持たない |

## 機能種別

システム制御 / プロセス管理

## 入力仕様

### 入力パラメータ（shutdown）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| -c | フラグ | No | 電源サイクル（再起動後に電源オフ/オン） | -h,-k,-p,-rと排他 |
| -f | フラグ | No | /var/run/noshutdownファイルの存在を無視 | なし |
| -h | フラグ | No | システム停止（halt） | -c,-k,-p,-rと排他 |
| -k | フラグ | No | 警告メッセージのみ送信し実際には停止しない | -c,-h,-p,-rと排他 |
| -n | フラグ | No | ファイルシステム同期をスキップ（-oが必要） | -oが必要 |
| -o | フラグ | No | reboot/haltコマンドを直接実行 | -c,-h,-p,-rのいずれかが必要 |
| -p | フラグ | No | 電源オフ | -c,-h,-k,-rと排他 |
| -q | フラグ | No | 警告メッセージを抑制 | なし |
| -r | フラグ | No | 再起動 | -c,-h,-k,-pと排他 |
| time | 文字列 | Yes | 停止時刻（now, +分数, yymmddhhmm） | 有効な時刻形式 |
| warning-message | 文字列 | No | ユーザへの警告メッセージ | なし |

### 入力パラメータ（reboot）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| -c | フラグ | No | 電源サイクル（RB_POWERCYCLE） | なし |
| -d | フラグ | No | ダンプ付き再起動（RB_DUMP） | なし |
| -D | フラグ | No | 既存ダンプをチェックしない | なし |
| -e | 文字列 | No | 環境変数の設定（key=value） | key=value形式 |
| -k | 文字列 | No | 次回起動カーネルの指定 | なし |
| -l | フラグ | No | haltをイベントとしてログ記録 | なし |
| -n | フラグ | No | sync無しで再起動 | なし |
| -N | フラグ | No | nextboot設定のリセット | なし |
| -o | 文字列 | No | 追加のnextboot設定 | なし |
| -p | フラグ | No | 電源オフ（RB_POWEROFF） | なし |
| -q | フラグ | No | 即座に再起動（RB_NOSYNC） | なし |
| -r | フラグ | No | reroot（RB_REROOT） | なし |

### 入力データソース

コマンドライン引数、標準入力（shutdown -で読み取り）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 警告メッセージ | 文字列 | ログインユーザへのwall(1)経由の通知 |
| syslogメッセージ | 文字列 | シャットダウン開始のログ記録 |
| nologinファイル | ファイル | /var/run/nologin（新規ログイン禁止） |
| nextboot.conf | ファイル | /boot/nextboot.conf（次回ブート設定） |

### 出力先

- ユーザ端末（wall(1)経由の警告メッセージ）
- syslog（LOG_AUTH）
- /var/run/nologin（シャットダウン5分前に作成）
- /boot/nextboot.conf（nextbootコマンド使用時）

## 処理フロー

### 処理シーケンス

```
shutdown:
1. root権限チェック
   └─ euid != 0の場合はエラー終了
2. オプション解析
   └─ -c/-h/-k/-p/-rの排他チェック
3. 停止時刻の計算
   └─ "now"=即時、"+N"=N分後、"yymmddhhmm"=絶対時刻
4. noshutdownファイルチェック
   └─ /var/run/noshutdownが存在し-f未指定ならエラー
5. デーモン化（fork）
   └─ バックグラウンドで警告ループを実行
6. 警告ループ
   └─ tlist[]の間隔表に従い定期的にwall(1)で警告送信
7. nologin作成
   └─ 停止5分前に/var/run/nologinを作成
8. 停止実行
   └─ die_you_gravy_sucking_pig_dog()で実際の停止処理

reboot:
1. オプション解析
   └─ howtoフラグの構築
2. nextboot設定
   └─ -k/-eオプション指定時は/boot/nextboot.confに書き込み
3. utmpx記録
   └─ シャットダウンイベントをutmpxに記録
4. syslog記録
   └─ rebootイベントをログに記録
5. init(8)へシグナル送信
   └─ howtoに応じたシグナル（SIGINT/SIGUSR1/SIGUSR2/SIGEMT/SIGWINCH）をinit(8)に送信
```

### フローチャート

```mermaid
flowchart TD
    A[shutdown/reboot起動] --> B{root権限?}
    B -->|No| C[エラー終了]
    B -->|Yes| D{コマンド種別}
    D -->|shutdown| E[停止時刻計算]
    D -->|reboot/halt| F[オプション解析]
    E --> G[fork でデーモン化]
    G --> H[警告ループ開始]
    H --> I{時刻到来?}
    I -->|No| J[wall で警告メッセージ送信]
    J --> K[sleep]
    K --> I
    I -->|Yes| L[nologin作成]
    L --> M[die_you_gravy_sucking_pig_dog]
    M --> N{-o指定?}
    N -->|Yes| O[reboot/halt直接実行]
    N -->|No| P[init にシグナル送信]
    F --> Q[nextboot.conf書き込み]
    Q --> R[utmpx/syslog記録]
    R --> S[init にシグナル送信]
    O --> T[システム停止/再起動]
    P --> T
    S --> T
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-72-01 | root権限必須 | shutdown/reboot/haltの実行にはroot権限が必要 | 常時 |
| BR-72-02 | noshutdown保護 | /var/run/noshutdownが存在する場合、-fオプションなしではshutdownを拒否 | shutdownコマンド実行時 |
| BR-72-03 | 警告間隔スケジュール | 10時間前から30秒前まで段階的に警告間隔が短くなる（tlist配列で定義） | shutdownコマンド実行時 |
| BR-72-04 | nologin作成タイミング | 停止5分前に/var/run/nologinファイルを作成し新規ログインを禁止 | shutdownコマンド実行時 |
| BR-72-05 | initシグナルマッピング | reboot=SIGINT, halt=SIGUSR1, poweroff=SIGUSR2, powercycle=SIGWINCH, reroot=SIGEMT | reboot/halt実行時 |

### 計算ロジック

警告間隔テーブル（tlist配列）:
- 10時間前: 5時間ごとに警告
- 5時間前: 3時間ごと
- 2時間前: 1時間ごと
- 1時間前: 30分ごと
- 30分前: 10分ごと
- 10分前: 5分ごと
- 5分前: 3分ごと
- 2分前: 1分ごと
- 1分前: 30秒ごと

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベースは使用しない |

### テーブル別操作詳細

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | 権限エラー | euid != 0 | "NOT super-user"メッセージでerrx終了 |
| 2 | noshutdown保護 | /var/run/noshutdown存在時 | メッセージ出力しexit(2) |
| EX_USAGE | 使用法エラー | 排他オプションの同時指定等 | usage()表示 |
| 1 | nextboot書き込み失敗 | /boot/nextboot.conf書き込みエラー | errまたはerrxで終了 |
| 1 | init(8)へのシグナル送信失敗 | kill(1, signo)失敗 | err(1, "SIG%s init") |

### リトライ仕様

リトライは行わない。エラー発生時は即座にエラーメッセージを出力して終了する。

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

nextboot.confへの書き込みは、一時ファイルに書き込み後にrename(2)でアトミックに置換する方式を採用している。fsync(2)による書き込み保証も行われる。

## パフォーマンス要件

特になし。shutdownの警告ループはsleep()を使用した低負荷の定期処理である。

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

- root権限チェックにより一般ユーザからの不正なシャットダウンを防止
- /var/run/noshutdownファイルによる意図しないシャットダウンの防止機構
- syslog(LOG_AUTH)によるシャットダウンイベントの監査ログ記録
- utmpxへのシャットダウンイベント記録
- boottrace(9)によるブートトレース記録

## 備考

- shutdownのdie_you_gravy_sucking_pig_dog()関数名はBSDの伝統的なユーモア
- poweroffコマンドはshutdownのハードリンクとして実装され、argv[0]の名前で動作を切り替える
- reboot、halt、nextbootも同一バイナリ（reboot.c）のハードリンクで、argv[0]により動作が変わる
- ZFSブート環境ではzfsbootcfgコマンドと連携してnextboot設定を行う

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | shutdown.c | `sbin/shutdown/shutdown.c` | interval構造体（警告間隔テーブル）、tlist配列の構造、H/M/Sマクロを理解する |

**読解のコツ**: `#define H *60*60` のようなマクロにより、`10 H` が `10*60*60`（36000秒）と展開される点に注意。tlist配列はtimeleft（残り時間）とtimetowait（待機時間）のペアで警告スケジュールを定義している。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | shutdown.c | `sbin/shutdown/shutdown.c` | main関数のオプション解析、poweroff特殊処理（argv[0]による分岐）、getoffset関数による時刻計算を理解する |

**主要処理フロー**:
1. **110-112行目**: root権限チェック（geteuid() != 0でエラー）
2. **122-137行目**: "poweroff"としての呼び出し時の特殊処理
3. **139-174行目**: getoptによるオプション解析
4. **181-188行目**: 排他オプションチェック
5. **225-228行目**: noshutdownファイルチェック
6. **245-256行目**: fork()によるデーモン化
7. **258行目**: loop()呼び出しで警告ループ開始

#### Step 3: rebootのエントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | reboot.c | `sbin/reboot/reboot.c` | main関数のプログラム名判定（reboot/halt/nextboot）、howtoフラグの構築、initへのシグナル送信を理解する |

**主要処理フロー**:
1. **57行目**: PATH_NEXTBOOT = "/boot/nextboot.conf"の定義
2. **74-76行目**: zfsbootcfg関数（ZFS環境でのnextboot対応）
3. **115-197行目**: write_nextboot関数（nextboot.confへのアトミック書き込み）
4. **233-249行目**: shutdown関数（init(8)へのシグナル送信、howtoフラグからシグナルへの変換）
5. **270-284行目**: プログラム名による動作切り替え（halt/nextboot/reboot）

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

```
shutdown (sbin/shutdown/shutdown.c)
    |
    +-- main()
    |     +-- getoffset()          # 停止時刻計算
    |     +-- fork()               # デーモン化
    |     +-- loop()               # 警告ループ
    |           +-- timewarn()     # 警告メッセージ送信
    |           +-- nolog()        # nologinファイル作成
    |           +-- die_you_gravy_sucking_pig_dog()  # 停止実行
    |                 +-- reboot(2) または kill(1, ...)
    |
reboot (sbin/reboot/reboot.c)
    |
    +-- main()
          +-- write_nextboot()     # nextboot.conf書き込み
          |     +-- zfsbootcfg()   # ZFS環境対応
          +-- shutdown()           # init(8)へシグナル送信
          |     +-- kill(1, signo)
          +-- reboot(2)            # 直接再起動
```

### データフロー図

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

コマンドライン引数     shutdown:                       ユーザ端末
(時刻, オプション) --> getoffset() --> loop()      --> (wall警告メッセージ)
                       |               |
                       |               +-----------> /var/run/nologin
                       |               |
                       |               +-----------> syslog (LOG_AUTH)
                       |
                       +-- reboot:
                           write_nextboot() -------> /boot/nextboot.conf
                           shutdown()        -------> init(8) (シグナル)
                                                     └-> reboot(2)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| shutdown.c | `sbin/shutdown/shutdown.c` | ソース | shutdownコマンドのメイン処理 |
| reboot.c | `sbin/reboot/reboot.c` | ソース | reboot/halt/nextbootコマンドのメイン処理 |
| shutdown.8 | `sbin/shutdown/shutdown.8` | マニュアル | shutdownコマンドのmanページ |
| reboot.8 | `sbin/reboot/reboot.8` | マニュアル | rebootコマンドのmanページ |
| nextboot.8 | `sbin/reboot/nextboot.8` | マニュアル | nextbootコマンドのmanページ |
