スリープをカーネル機能拡張からコントロールする
insomnia=不眠症。不眠症というだけあって、InsomniaXはMacBookのスリープを強力に制限してくれる。
- ディスプレイを閉じた時のスリープ。
- 電源ボタンを押して、終了ダイアログからのスリープ。
- アップルメニューからのスリープ。
- command-option-Ejectによるショートカットのスリープ。
- pmset sleepnowコマンドによるスリープ。
- AppleScriptによるスリープ。
以上、ありとあらゆるスリープが無効となるのだ。スリープする唯一の方法は、InsomniaXのアイコンメニューから、Sleep System を選択するのみ。
ところで今時のMacBookは、Wake on Demandによるスリープ中のアクセスが保証されているので、あえて不眠症にする理由はほとんどない。しかし、うっかりディスプレイを閉じておくと、Wake on Demandの恩恵は受けられなくなってしまう。そんな時、InsominaXが起動していれば、ディスプレイを閉じてスリープした状態からでも、Wake on Demandによるアクセスが可能になる。そう思って、やってみたのが以下の日記。
ディスプレイを閉じてしまうと、画面は見えない、かつキーボードやトラックパッドも使えなくなってしまうので、AppleScriptでInsomniaXの Sleep System を実行するようにしてみたのだ。ところが、InsomniaXのアイコンメニューは、GUIスクリプティングを使っても操作できなかった*1。そのため、cliclickというマウスのクリックをシミュレートするコマンドを利用して操作するようにしたのだ。
当初はうまく動いていたが、欠点がある。cliclickコマンドは、ディスプレイ左上を原点とした絶対座標でクリック位置を指定するので、アイコンメニューの並びが変わると、全く無意味な操作になってしまうのだ。アイコンメニューの並び順を指定するツールもあるが、新規インストールや削除することもあるので、InsomniaXのアイコン位置が固定される訳ではない。何より、開発したマシン環境限定のスクリプトとなってしまう。他の環境で使おうと思ったら、cliclickコマンドの座標を調整しなくてはならない今イチなスクリプトだった。
どうにかして、もっとスマートにInsomniaXをコントロールする方法はないのか?以前から模索していたのだが、久々にInsomniaXの本家ページを閲覧して、そのヒントに気付いた。
ヒント
2つの情報がヒントとなった。
64ビット対応
- Snow Leopardの64ビット対応のため、Insomnia_r6.kext というカーネル機能拡張をダウンロードして、アップデートしておく必要があるらしい。
カーネル機能拡張のコントロール
- InsomniaX.appに頼らないで、Insomnia.kextをコントロールする方法は、たった二つしかない。
kextloadとkextunload。
- なるほど!さっそく試してみた。
$ sudo kextload /Applications/InsomniaX.app/Contents/Resources/Insomnia_r6.kext
Password:
/Applications/InsomniaX.app/Contents/Resources/Insomnia_r6.kext failed to load - (libkern/kext) authentication failure (file ownership/permissions); check the system/kernel logs for errors or try kextutil(8).
- しかし、残念ながらエラー発生!どうやらカーネルはアクセス権限に厳格らしい。
- sudoで実行するのはもちろんのこと、ロードするカーネル機能拡張も 所有者: root、グループ: wheel でないと受け付けてくれないらしい。
- それではInsomniaXはどうやってロードしているのだろう?と調べてみると...
$ ls -l ~/Library/Application\ Support/InsomniaX/
total 0
drwxr----- 3 root wheel 102 5 31 19:37 Insomnia_r6.kext/
- ~/Library/Application Support/InsomniaX/ フォルダに、すでにアクセス権限を変更した Insomnia_r6.kext が見つかった。
- 素直にこれを活用させてもらうことにした。
$ sudo kextload ~/Library/Application\ Support/InsomniaX/Insomnia_r6.kext
- 実行はすぐに完了した。どうやら、ロードできたようだ。
- ディスプレイを閉じてみた。
- 全くスリープする様子はない。
成功した!(ようだ)
- カーネル機能拡張のロード状況を確認するコマンドもある。
$ kextstat
$ kextstat|grep -v apple Index Refs Address Size Wired Name (Version)113 0 0x5c86f000 0xe000 0xd000 com.iospirit.driver.rbiokithelper (1.5.4) <62 35 29 5 4 3 1> 115 0 0x5bfc4000 0x3000 0x2000 com.eltima.ElmediaPlayer.kext (1.0) <4 1> 116 0 0x55347000 0x5000 0x4000 com.google.driver.Gild (1.0.0) <5 4 3 1> 117 0 0x5c49e000 0x18000 0x17000 org.pqrs.driver.KeyRemap4MacBook (7.3.0) <29 5 4 3 1> 127 0 0x5c1f2000 0x2000 0x1000 com.bresink.driver.BRESINKx86Monitoring (2.0) <12 11 10> 128 3 0x5c8d1000 0x25000 0x24000 org.virtualbox.kext.VBoxDrv (3.2.8) <7 5 4 3 1> 129 0 0x5c397000 0x7000 0x6000 org.virtualbox.kext.VBoxUSB (3.2.8) <128 41 35 7 5 4 3 1> 130 0 0x5c48c000 0x4000 0x3000 org.virtualbox.kext.VBoxNetFlt (3.2.8) <128 7 5 4 3 1> 131 0 0x5c1f8000 0x3000 0x2000 org.virtualbox.kext.VBoxNetAdp (3.2.8) <128 5 4 1> 204 0 0x5b44f000 0x3000 0x2000 com.protech.nosleep (1.2.1) <4 3> 283 0 0x55307000 0x3000 0x2000 org.binaervarianz.driver.insomnia (1.0.0d1) <4 3 1>
- 一番最後の行に、insomniaが確認できた!
- Insomnia.kextを無効にするには、loadをunloadに変更して実行ればOK。
$ sudo kextunload ~/Library/Application\ Support/InsomniaX/Insomnia_r6.kext
一時的にスリープを許可する
- 有効・無効の設定方法は分かった。しかし、自分が操作したいのはInsomniaXのSleep System。
- 一体どうやって、Insomnia.kextにスリープを依頼しているのだろう?
- Insomnia.kextの本家ページでも調べてみたが、kextload、kextunload以外の手段は無さそうだ。
- そのヒントはkernel.logに隠されていた。以下は、InsomniaXでSleep Systemを実行したときのログ。
Jul 14 04:30:32 zari-MacBook kernel[0]: Insomnia: kIOPMEnableClamshell sent to root Jul 14 04:30:32 zari-MacBook kernel[0]: Insomnia: Lid close is now processed again. Jul 14 04:30:32 zari-MacBook kernel[0]: Insomnia finished Jul 14 04:30:32 zari-MacBook kernel[0]: ========================================== Jul 14 04:30:33 zari-MacBook kernel[0]: AFPSleepWakeHandler: going to sleep Jul 14 04:30:33 zari-MacBook kernel[0]: enet_event_func - vendor 1, class 1, subclass 2, event code 14 Jul 14 04:30:34: --- last message repeated 1 time --- Jul 14 04:30:34 zari-MacBook kernel[0]: enet_event_func - vendor 1, class 1, subclass 2, event code 15 Jul 14 04:30:35: --- last message repeated 1 time --- Jul 14 04:30:35 zari-MacBook kernel[0]: enet_event_func - vendor 1, class 1, subclass 2, event code 12 Jul 14 04:30:36 zari-MacBook kernel[0]: System Sleep Jul 14 04:30:37 zari-MacBook kernel[0]: Wake reason = UHC3 Jul 14 04:30:37 zari-MacBook kernel[0]: System Wake Jul 14 04:30:37 zari-MacBook kernel[0]: Previous Sleep Cause: 5 Jul 14 04:30:37 zari-MacBook kernel[0]: The USB device Apple Internal Keyboard / Trackpad (Port 2 of Hub at 0x5d000000) may have caused a wake by issuing a remote wakeup (2) Jul 14 04:30:37 zari-MacBook kernel[0]: AirPort: Link Down on en1. Reason 4 (Disassociated due to inactivity). Jul 14 04:30:37 zari-MacBook kernel[0]: enet_event_func - vendor 1, class 1, subclass 2, event code 12 Jul 14 04:30:38 zari-MacBook kernel[0]: Insomnia:init Jul 14 04:30:38 zari-MacBook kernel[0]: Insomnia:start Jul 14 04:30:38 zari-MacBook kernel[0]: Insomnia: kIOPMDisableClamshell sent to root Jul 14 04:30:38 zari-MacBook kernel[0]: Insomina: enabled Jul 14 04:30:38 zari-MacBook kernel[0]: ========================
- 何と!スリープする前に、Insomnia.kextは一旦終了しているように見える。(kextunload)
- そしてスリープが解除されると、Insomnia.kextは再び動き出しているように見える。(kextload)
- 同じような動作になるように、コマンドで実行してみた。
$ pmset sleepnow Unable to sleep system: error 0xe00002bc # Insomnia.kextがロードされていると、 # 上記のようにエラーが発生してスリープできない # ところがkextunloadとkextloadで挟んであげると... $ sudo kextunload ~/Library/Application\ Support/InsomniaX/Insomnia_r6.kext;\Sleeping now...できた!
- なんと、スリープする前に一旦 Insomnia.kext をアンロードしていただけ、だったのだ。
- コマンドは、スリープ復帰後にまた Insomnia.kext をロードするので有効になる。
- だからディスプレイを閉じた状態でも、Wake on Demandで接続して操作もできるのだ。
これで、InsomniaX.appに頼らずに、スリープの有効・無効を自由にコントロールできるようになった!
- Insomnia.kextはあくまでもスリープを無効にすることに特化したカーネル機能拡張で、
- InsomniaX.appは、それをロード・アンロードするタイミングを調整するだけで、便利な環境を提供してくれていたのだ。