転載・引用について

ユーザ用ツール

サイト用ツール


tweet:2026:0706_20260706_1

macOS LaunchAgent で Thunderbolt NVMe を keepalive する方法(まとめ)

問題の概要

  • sudo pmset -a disksleep 0 を実行
  • /Volumes/Home/.keepalive を作成
    • dd if=/dev/zero of=/Volumes/Home/.keepalive count=1 bs=4096
      • bsはDiskによる。HDDで古いデバイスなら512でも良い。
    • /Volumes/Homeは外付けThnuderbolt SSD
  • ~/bin/keepnvmeawake.sh は以下の通り
    • #!/bin/bash
      #dd if=/Volumes/Home/.keepalive of=/dev/null bs=4k count=1 2>/dev/null
      /usr/bin/head -c 1 /Volumes/Home/.keepalive >/dev/null 2>&1 || true
      exit 0

この条件下でThunderbolt NVMe 上に置いた keepalive スクリプトを LaunchAgent で定期実行しようとしたところ、

  1. 手動実行では常に成功(exit 0)
  2. LaunchAgent から実行すると exit 126(EX_NOPERM)
  3. スクリプトは存在し、実行権限もあり、NVMe は unmount されていない
  4. Gatekeeper の quarantine も削除済み
  5. 署名を付けても、削除しても改善しない

という状況が発生した。

原因

LaunchAgent(launchd)は「実行ファイルの実体がどこにあるか」を安全性判定に使う。

  • 内蔵 SSD → 安全な実行元
  • 外付け NVMe / USB / Thunderbolt → 安全ではない実行元
  • symlink → リンク先の実体の場所で判定する

つまり:

  • ~/bin/keepnvmeawake.sh は ~/bin が symlink であるため、Sandboxに弾かれる
    • script 実体は Thunderbolt NVMe 上
      • launchd の sandbox が 「外付けディスク上の実行ファイル」 と判定
      • bash がスクリプトを開く段階で exit 126 を返す

ここで重要なのは

  • NVMe のデータファイル (.keepalive) は常に読める
  • NVMe 上の「実行ファイル」だけが launchd に拒否される

という点。

解決策の原理

launchd がチェックするのは「実行ファイルの場所」だけであり、スクリプト内部で外付け NVMe を読むことは sandbox の対象外。

よって、

  • 実行ファイルだけ内蔵 SSD に置く
  • NVMe にアクセスするのは「スクリプト内部の処理」だけにする

これが launchd の sandbox を完全に回避する唯一の方法。

最終的な解決策(最もシンプル)

スクリプトを使わず、LaunchAgent から 直接 /usr/bin/head を呼ぶ。 /usr/bin/head は内蔵 SSD にあるため、launchd が安全と判断する。

com.keepnvmeawake.plist(最終版)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.keepnvmeawake</string>
 
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/head</string>
        <string>-c</string>
        <string>1</string>
        <string>/Volumes/Home/.keepalive</string>
    </array>
 
    <key>StartInterval</key>
    <integer>60</integer>
 
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

動作確認

launchctl

$ launchctl unload ~/Library/LaunchAgents/com.keepnvmeawake.plist
$ launchctl load ~/Library/LaunchAgents/com.keepnvmeawake.plist
$ launchctl list | grep keepnvmeawake

exit code = 1 になることがあるが、これは launchd が「即終了ジョブ」を失敗扱いにする仕様であり、Thunderbolt NVMe の keepalive には影響しない。

fs_usage

# sudo fs_usage -f filesystem | grep .keepalive

1分ごとに以下のような I/O が出ていれば 完全に成功:

read /Volumes/Home/.keepalive

exit code が 1 になる理由

launchd は「ジョブの最低実行時間」を内部的に持っており、head のような 即終了ジョブは「失敗扱い(exit 1)」になる。

これは 正常挙動であり、keepalive の動作には影響しない。

最終結論

  • launchd は外付け NVMe 上の「実行ファイル」を拒否する
  • NVMe 上のデータファイルを読むことは拒否しない
  • よって、実行ファイルだけ内蔵 SSD に置く必要がある
  • 最もシンプルな解決策は plist から直接 /usr/bin/head を呼ぶこと
  • fs_usage に I/O が出ていれば Thunderbolt NVMe keepalive は完全に成功
このウェブサイトはクッキーを使用しています。 Webサイトを使用することで、あなたはあなたのコンピュータにクッキーを保存することに同意します。 また、あなたはあなたが私たちのプライバシーポリシーを読んで理解したことを認めます。 同意しない場合はウェブサイトを離れてください。クッキーに関する詳細情報
tweet/2026/0706_20260706_1.txt · 最終更新: by seirios

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki