【Rust】手軽にLinuxの電源操作ができるWebUIツール「powe_rs」

この記事では「powe_rs」というツールをご紹介します。これはRust言語で開発された軽量なWebアプリケーションで、ブラウザから簡単にLinuxマシンの電源操作ができるようにする実験的なプロジェクトです。依存関係も少なく、シンプルな設計になっているため、特にラズベリーパイのような小型Linuxマシンでの使用に適しています。

## powe_rsの主な機能

powe_rsは、シンプルながらも実用的な機能を提供しています。

- ブラウザベースのシンプルなWebUIからLinuxの電源オフとリブート操作が可能
- 誤操作防止のための確認ダイアログを表示
- 電源操作のスケジュール設定機能（特定の時間に電源オフ/リブートを予約可能）
- systemdサービスとしての簡単なインストール・設定
- 待ち受けIPアドレスとポート番号のカスタマイズ

WebUIは非常にシンプルで、直感的な操作が可能です。

![powe_rsのWebUI画面](https://github.com/omid/powe_rs/raw/refs/heads/master/assets/screen.apng)

## セキュリティに関する重要な注意点

このツールを使用する際には、セキュリティ面での考慮が非常に重要です。以下の点に必ず注意してください。

- powe_rsは**プライベートネットワーク内での利用のみ**を想定しています
- インターネットや信頼できないネットワークには絶対に公開しないでください
- システムの電源を操作できる強力な機能を持つため、ファイアウォールやVPNなどを用いて、アクセスを適切に制限することが必要です

## インストール方法

powe_rsをインストールするには、主に2つの方法があります。それぞれの方法について、順を追って説明します。

### 1. ビルド済みパッケージを使用する方法（推奨）

最も簡単な方法は、開発者が提供しているビルド済みのバイナリを利用することです。

1. [powe_rsのGitHubリリースページ](https://github.com/omid/powe_rs/releases)にアクセスします
2. お使いのマシンのCPUアーキテクチャに合ったパッケージをダウンロードします
   - Intel/AMD系のPCであれば`amd64`
   - ラズベリーパイなどのARMデバイスであれば`aarch64`または`armv7`

### 2. ソースコードからビルドする方法

Rustの開発環境が整っていれば、ソースコードからビルドすることも可能です。この方法では、最新の機能を利用することができます。

```bash
# リポジトリをクローン
git clone https://github.com/omid/powe_rs.git
cd powe_rs

# リリースモードでビルド
cargo build --release
```

ビルドが成功すると、`target/release/`ディレクトリ内に実行ファイル`powe_rs`が生成されます。

## 使い方

powe_rsは、systemdサービスとして実行する方法と、一時的に手動で起動する方法があります。状況に応じて、適切な方法を選択しましょう。

### systemdサービスとしてインストール・実行

Linuxマシンの起動時に自動的にpowe_rsを起動させたい場合は、systemdサービスとして登録するのが便利です。

```bash
# デフォルトポート (54321) でサービスをインストール
sudo ./powe_rs install

# IPアドレスとポートをカスタム指定してインストール
sudo ./powe_rs install -l 127.0.0.1:8080
sudo ./powe_rs install -l 8080  # IPアドレスを省略すると 0.0.0.0 (全インターフェース) になります
```

インストール後、ブラウザで `http://<マシンのIPアドレス>:54321` (または指定したアドレス・ポート) にアクセスすると、powe_rsのWebUIが表示されます。

### 手動での起動

一時的に利用したい場合など、systemdサービスとして登録せずに手動で起動することも可能です。

```bash
# デフォルト設定で起動
./powe_rs serve

# IPアドレスとポートをカスタム指定して起動
./powe_rs serve -l 127.0.0.1:8080
./powe_rs serve -l 8080
```

### テストモード（Dry Run）

実際に電源操作コマンドを実行せずに動作を確認したい場合は、環境変数 `DRY=true` を設定して起動します。これは開発やテストの段階で非常に役立ちます。

```bash
DRY=true ./powe_rs serve
```

### アンインストール

systemdサービスとして登録したpowe_rsが不要になった場合は、以下のコマンドでアンインストールできます。

```bash
# サービス定義のみを削除
sudo ./powe_rs uninstall

# サービス定義と実行バイナリの両方を削除
sudo ./powe_rs uninstall -a
```

### ヘルプ表示

利用可能なコマンドやオプションの詳細は、ヘルプコマンドで確認できます。

```bash
./powe_rs -h
```

## powe_rsの内部構造

powe_rsは比較的シンプルな構造をしています。主要な部分のコードを見ていきましょう。

### Webサーバー部分

HTTPリクエストを待ち受け、処理するWebサーバー部分は以下のように実装されています。

```rust
fn serve(ip: &str, port: u16) {
    let bind_addr = if ip.contains(":") {
        // IPv6 アドレスの場合
        format!("[{}]:{}", ip, port)
    } else {
        // IPv4 アドレスの場合
        format!("{}:{}", ip, port)
    };
    let listener = std::net::TcpListener::bind(&bind_addr).expect("Failed to bind address");
    println!("Listening on http://{}:{}", ip, port); // 実際のアドレスとポートを表示
    for mut stream in listener.incoming().flatten() {
        std::thread::spawn(move || {
            let mut buffer = [0; 1024];
            let _ = stream.read(&mut buffer); // リクエスト読み込み
            let request = String::from_utf8_lossy(&buffer);

            // リクエスト内容に応じて処理を分岐
            let response = if request.contains("POST /poweroff") {
                let when = extract_body(&request); // リクエストボディからスケジュール情報を抽出
                SystemCtl::poweroff(when.as_deref()); // 電源オフ処理
                "HTTP/1.1 204 NO CONTENT".to_string()
            } else if request.contains("POST /reboot") {
                let when = extract_body(&request);
                SystemCtl::reboot(when.as_deref());   // リブート処理
                "HTTP/1.1 204 NO CONTENT".to_string()
            } else {
                // GETリクエストなど、上記以外はHTMLページを返す
                format!("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n{}", HTML)
            };
            let _ = stream.write(response.as_bytes()); // レスポンス送信
        });
    }
}
```

このコードの特徴的な点は、以下の通りです。

- Rustの標準ライブラリに含まれる`std::net::TcpListener`を使用して、シンプルなTCPサーバーを構築しています
- 各リクエストに対して新しいスレッドを生成し、並行処理を実現しています
- リクエストのパスに応じて処理を分岐させ、`/poweroff`や`/reboot`のPOSTリクエストに対応する電源操作を実行しています
- その他のリクエスト（GETリクエストなど）に対しては、HTMLページを返して画面表示を行います

### systemctl連携部分

実際にLinuxシステムの電源を操作するための`systemctl`コマンドを呼び出す部分は、以下のように実装されています。

```rust
pub fn poweroff(when: Option<&str>) {
    Self::do_power_action("poweroff", when, "power off");
}
pub fn reboot(when: Option<&str>) {
    Self::do_power_action("reboot", when, "reboot");
}

fn do_power_action(action: &str, when: Option<&str>, log_label: &str) {
    let mut cmd = Self::systemctl_cmd(); // systemctlコマンドの準備
    cmd.arg(action); // "poweroff" や "reboot" などのアクションを指定
    let when = when.unwrap_or_default(); // スケジュール指定 (なければ空文字列)
    if !when.is_empty() {
        cmd.arg("--when"); // スケジュールオプション
        cmd.arg(when);     // スケジュール時間
    }
    // ログ出力
    match when {
        "cancel" => SystemCtl::log(&format!("cancel scheduled {log_label}")),
        "" => SystemCtl::log(log_label),
        w => SystemCtl::log(&format!("{log_label} scheduled at {w}")),
    }
    Self::run_cmd(cmd); // コマンド実行
}
```

このコードでは、以下のポイントが重要です。

- `std::process::Command`（コード内の`SystemCtl::systemctl_cmd()`で生成）を使用して、外部コマンドである`systemctl`を実行しています
- スケジュール機能は`systemctl`の`--when`オプションを利用しており、Linuxの標準的な仕組みに則っています
- 操作のタイミングは即時実行、スケジュール実行、スケジュールキャンセルの3パターンに対応しています

## WebUIの詳細

powe_rsのWebUIは、単一のHTMLファイルにHTML、CSS、JavaScriptがまとめて記述されています。

- 「Power off」と「Reboot」の2つの主要操作ボタン
- 誤操作防止のための確認ダイアログ
- スケジュール設定用のインターフェース
  - 即時実行
  - 特定時刻に実行（時間指定）
  - スケジュールのキャンセル

## まとめ

「powe_rs」は、Linuxマシンの電源管理をWebブラウザから手軽に行えるようにするツールです。Rustで書かれており、動作が軽量で依存関係も少ないため、ラズベリーパイなどの小型Linuxデバイスでも快適に動作します。

ただし、システムの電源を操作できる強力な機能を持つため、セキュリティには十分注意し、信頼できるプライベートネットワーク内でのみ使用するようにしてください。

興味を持たれた方は、[powe_rsのGitHubリポジトリ](https://github.com/omid/powe_rs)を訪れて、自分の環境に導入してみてはいかがでしょうか。

## 参考リンク

- [powe_rs GitHub リポジトリ](https://github.com/omid/powe_rs)
- [systemctl コマンドリファレンス](https://www.freedesktop.org/software/systemd/man/systemctl.html)