# 機能設計書 70-cron

## 概要

本ドキュメントは、FreeBSDの定期実行ジョブスケジューラであるcronの機能設計を記述する。cronデーモンはcrontabファイルに定義されたスケジュールに基づいてコマンドを自動実行する。

### 本機能の処理概要

**業務上の目的・背景**：システム管理やアプリケーション運用において、定期的なタスク（バックアップ、ログローテーション、データ集計、監視チェック等）の自動実行は不可欠である。cronはUNIXの伝統的なジョブスケジューラとして、分・時・日・月・曜日の5フィールド（または秒を含む6フィールド）でスケジュールを定義し、指定時刻にコマンドを実行する。

**機能の利用シーン**：定期バックアップの自動実行、ログファイルのローテーション、システム監視スクリプトの定期実行、データベースメンテナンス、メール配信のスケジューリング、パッケージ更新チェックなど、あらゆる定期タスクの自動化に使用される。

**主要な処理内容**：
1. crontabデータベースの読み込みとメンテナンス
2. 分（または秒）単位でのジョブスケジュールチェック
3. マッチしたジョブの実行（fork/exec）
4. ジョブ出力の取得とメール配信
5. DST（夏時間）切り替えへの対応
6. @rebootジョブの起動時実行
7. ユーザ別crontabの管理（crontabコマンド）
8. ジッター機能によるジョブ実行時刻の分散

**関連システム・外部連携**：sendmail等のMTAを通じてジョブ出力をメール配信する。syslogにログを記録する。PAM/login.capによるユーザリソース制限を適用する。

**権限による制御**：cronデーモンはroot権限で動作する。各ジョブはcrontabのオーナーのUID/GIDで実行される。/var/cron/allow と /var/cron/deny でcrontab操作の許可/拒否を制御する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能にはGUI画面の関連はない |

## 機能種別

ジョブスケジューリング / 定期実行管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| -j jitter | int | No | ジョブ実行のジッター（秒、ユーザジョブ） | 正の整数 |
| -J rootjitter | int | No | ジョブ実行のジッター（秒、rootジョブ） | 正の整数 |
| -m mailto | string | No | デフォルトのメール送信先 | メールアドレス |
| -n | flag | No | デーモン化しない | なし |
| -s | flag | No | DST変更に対応 | なし |
| -o | flag | No | 旧互換モード | なし |
| -x debugflags | string | No | デバッグフラグ | 有効なフラグ名 |

### 入力データソース

- /var/cron/tabs/: ユーザ別crontabファイル
- /etc/crontab: システムcrontab
- /var/cron/allow: crontab使用許可リスト
- /var/cron/deny: crontab使用拒否リスト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ジョブ実行結果 | int | コマンドの終了ステータス |
| ジョブ出力 | text | コマンドの標準出力/標準エラー |
| syslogメッセージ | string | ジョブ実行ログ |
| メール | text | ジョブ出力のメール配信 |

### 出力先

- 実行されるコマンドの出力（メールとして配信）
- syslog: ジョブ実行記録
- メール: ジョブ出力（MAILTO環境変数で制御）

## 処理フロー

### 処理シーケンス

```
1. 起動・初期化
   └─ PIDファイル作成、UID設定、カレントディレクトリ設定、デーモン化
2. データベース読み込み
   └─ /var/cron/tabs/ と /etc/crontab の読み込み、エントリ解析
3. @rebootジョブ実行
   └─ WHEN_REBOOTフラグのエントリをキューに追加して実行
4. cron_sync
   └─ 次の分（または秒）境界まで待機
5. メインループ
   a. cron_sleep: TargetTimeまでスリープ（データベース変更監視付き）
   b. データベース再読み込み（60秒ごと、または変更検出時）
   c. cron_tick: 現在時刻とcrontabエントリのマッチング
   d. マッチしたジョブをキューに追加
   e. ジョブキュー実行（fork/exec）
   f. TargetTime += 60（または1秒）
6. DST対応
   └─ タイムゾーン変更検出時のジョブ実行調整
```

### フローチャート

```mermaid
flowchart TD
    A[cron起動] --> B[PIDファイル・デーモン化]
    B --> C[データベース読み込み]
    C --> D[@rebootジョブ実行]
    D --> E[cron_sync: 分境界待機]
    E --> F{メインループ}
    F --> G[cron_sleep: スリープ]
    G --> H{DB変更?}
    H -->|Yes| I[データベース再読み込み]
    H -->|No| J[cron_tick: マッチング]
    I --> J
    J --> K{マッチあり?}
    K -->|Yes| L[ジョブキュー追加]
    K -->|No| M[TargetTime更新]
    L --> N[ジョブ実行 fork/exec]
    N --> M
    M --> F
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-70-01 | 5/6フィールド形式 | 秒(オプション) 分 時 日 月 曜日 の形式でスケジュール定義 | crontabエントリ |
| BR-70-02 | DOM/DOWの論理和 | day-of-monthとday-of-weekは論理和で評価（両方指定時は「または」） | DOM/DOW両方が*でない場合 |
| BR-70-03 | @reboot特殊記法 | @rebootはcronデーモン起動時に1回実行 | WHEN_REBOOTフラグ |
| BR-70-04 | @every_second | SEC_RESフラグにより秒単位解像度での実行が可能 | 秒フィールド使用時 |
| BR-70-05 | DST対応 | ST→DST変更時はスキップされた時間帯のジョブをRUN_ATフラグで早期実行 | -sオプション有効時 |
| BR-70-06 | DST対応（逆） | DST→ST変更時はNOT_UNTILフラグで重複実行を防止 | -sオプション有効時 |
| BR-70-07 | INTERVALモード | @every_Nsの形式でN秒間隔の実行が可能 | INTERVALフラグ |
| BR-70-08 | MAIL_WHEN_ERR | エラー時のみメール配信するオプション | MAIL_WHEN_ERRフラグ |

### 計算ロジック

TargetTime更新: `TargetTime += (secres != 0) ? 1 : 60`（秒解像度時は1秒、通常は60秒）

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

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

cron内部でcron_db構造体をインメモリデータベースとして使用する。

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| DB読み込み | cron_db | INSERT/UPDATE | crontabファイルからのエントリ読み込み |
| DB更新 | cron_db | UPDATE | ファイル変更時の再読み込み |
| ジョブキュー | job queue | INSERT | マッチしたジョブのキュー追加 |

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

#### cron_db

| 操作 | 項目 | 更新値・取得条件 | 備考 |
|-----|------|-----------------|------|
| load | user.name | crontabファイル名 | ユーザ名 |
| load | user.mtime | ファイル更新時刻 | 変更検出に使用 |
| load | entry.* | crontabエントリ | スケジュール+コマンド |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EEXIST | PID競合 | cronが既に起動中 | "cron already running" ログ出力後exit |
| - | crontabパースエラー | 文法エラー | エラー行をスキップ、ログ出力 |
| - | ジョブ実行失敗 | fork()失敗 | ログ出力、次回再試行 |
| - | メール配信失敗 | MTA不在 | ジョブ出力は破棄 |

### リトライ仕様

ジョブ実行そのもののリトライは行わない。次回スケジュールでマッチすれば再度実行される。

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

トランザクション管理は不要。

## パフォーマンス要件

- cron_tickのスケジュールマッチングはビット配列のビット演算で高効率
- データベース再読み込みは60秒ごと（またはファイル変更検出時）に限定
- madvise(MADV_PROTECT)によりcronプロセスのOOMキル防止

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

- ジョブはcrontabオーナーのUID/GIDで実行される
- /var/cron/allow と /var/cron/deny によるcrontabアクセス制御
- LOGIN_CAP定義時はlogin.capによるリソース制限を適用
- crontabの編集時はMD5チェックサムで改竄検出
- crontab実行前にユーザの存在確認（passwd参照）

## 備考

- Paul Vixie氏のcron実装をベースとしている
- crontab(1)コマンドでユーザ別のcrontab管理（-l: 一覧、-e: 編集、-r: 削除）
- FreeBSD版はINTERVAL、SEC_RES（秒解像度）など独自拡張あり

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | structs.h | `usr.sbin/cron/cron/structs.h` | entry構造体（22-57行目）、user構造体（66-71行目）、cron_db構造体（73-76行目） |
| 1-2 | macros.h | `usr.sbin/cron/cron/macros.h` | 定数マクロ（FIRST_MINUTE, DOM_COUNT等） |
| 1-3 | externs.h | `usr.sbin/cron/cron/externs.h` | グローバル変数宣言 |

**読解のコツ**: entry構造体はunion（無名共用体）を使用している。通常のcrontabエントリではbitstr_tビット配列で分・時・日・月・曜日のスケジュールを表現し、INTERVALモードではlastexit/interval/childで間隔実行を管理する。flags フィールドのビットフラグ（DOM_STAR, DOW_STAR, WHEN_REBOOT, SEC_RES, INTERVAL等）で動作モードを制御する。

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

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

**主要処理フロー**:
1. **32-40行目**: 関数宣言（usage, run_reboot_jobs, cron_tick, cron_sync, cron_sleep, cron_clean等）
2. **45-49行目**: グローバル変数（database, last_time, dst_enabled, dont_daemonize, pfh）
3. **72-92行目**: open_pidfile() - PIDファイルの排他制御
4. **94-185行目**: main() - 初期化、デーモン化、メインループ
5. **108行目**: parse_args() - コマンドライン引数解析
6. **142行目**: load_database() - crontabデータベース読み込み
7. **143行目**: run_at_secres() - 秒解像度チェック
8. **145行目**: run_reboot_jobs() - @rebootジョブ実行
9. **147-184行目**: メインループ（cron_sleep → load_database → cron_tick → TargetTime更新）
10. **187-203行目**: run_reboot_jobs() - WHEN_REBOOTフラグジョブの実行
11. **206-299行目**: cron_tick() - スケジュールマッチング（DST対応含む）

#### Step 3: ジョブ実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | do_command.c | `usr.sbin/cron/cron/do_command.c` | ジョブの実行処理（fork/exec、出力取得、メール配信） |
| 3-2 | job.c | `usr.sbin/cron/cron/job.c` | ジョブキュー管理 |

#### Step 4: crontabパーサを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | entry.c | `usr.sbin/cron/lib/entry.c` | crontabエントリのパース処理 |
| 4-2 | env.c | `usr.sbin/cron/lib/env.c` | 環境変数の処理 |
| 4-3 | misc.c | `usr.sbin/cron/lib/misc.c` | ユーティリティ関数 |

#### Step 5: crontabコマンドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | crontab.c | `usr.sbin/cron/crontab/crontab.c` | crontab管理コマンド（-l, -e, -r） |

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

```
cron main() [cron.c]
    |
    +-- open_pidfile() [cron.c:72-92]
    +-- set_cron_uid() / set_cron_cwd()
    +-- daemon()
    +-- load_database() [database.c]
    |       +-- ユーザcrontab読み込み (/var/cron/tabs/*)
    |       +-- システムcrontab読み込み (/etc/crontab)
    |       +-- entry解析 [lib/entry.c]
    |               +-- get_list() → ビット配列設定
    |
    +-- run_reboot_jobs() [cron.c:187-203]
    |       +-- job_add() [job.c]
    |       +-- job_runqueue() [job.c]
    |               +-- do_command() [do_command.c]
    |
    +-- メインループ
            +-- cron_sleep() [cron.c]
            +-- load_database() [database.c]（変更時）
            +-- cron_tick() [cron.c:206-299]
            |       +-- ビット配列マッチング
            |       +-- DST検出・補正
            |       +-- job_add() [job.c]
            +-- job_runqueue() [job.c]
                    +-- do_command() [do_command.c]
                            +-- fork()
                            +-- exec() → コマンド実行
                            +-- 出力取得 → メール配信
```

### データフロー図

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

/var/cron/tabs/* ──────▶ load_database() ──────▶ cron_db構造体
/etc/crontab ──────────┘        |
                                v
システム時刻 ──────────▶ cron_tick() ───────────▶ ジョブキュー
                                |
                                v
ジョブキュー ──────────▶ do_command() ──────────▶ コマンド実行
                                |                   syslogログ
                                v                   メール配信
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cron.c | `usr.sbin/cron/cron/cron.c` | ソース | cronデーモンメイン |
| cron.h | `usr.sbin/cron/cron/cron.h` | ヘッダ | 共通ヘッダ |
| structs.h | `usr.sbin/cron/cron/structs.h` | ヘッダ | データ構造定義 |
| macros.h | `usr.sbin/cron/cron/macros.h` | ヘッダ | 定数マクロ |
| database.c | `usr.sbin/cron/cron/database.c` | ソース | DB読み込み |
| do_command.c | `usr.sbin/cron/cron/do_command.c` | ソース | ジョブ実行 |
| job.c | `usr.sbin/cron/cron/job.c` | ソース | ジョブキュー |
| user.c | `usr.sbin/cron/cron/user.c` | ソース | ユーザcrontab管理 |
| popen.c | `usr.sbin/cron/cron/popen.c` | ソース | パイプ操作 |
| entry.c | `usr.sbin/cron/lib/entry.c` | ソース | エントリパーサ |
| env.c | `usr.sbin/cron/lib/env.c` | ソース | 環境変数処理 |
| misc.c | `usr.sbin/cron/lib/misc.c` | ソース | ユーティリティ |
| crontab.c | `usr.sbin/cron/crontab/crontab.c` | ソース | crontabコマンド |
| cron.8 | `usr.sbin/cron/cron/cron.8` | マニュアル | cronマニュアル |
| crontab.1 | `usr.sbin/cron/crontab/crontab.1` | マニュアル | crontabマニュアル |
| crontab.5 | `usr.sbin/cron/crontab/crontab.5` | マニュアル | crontab形式マニュアル |
