В статье описан способ автоматического отключения/включения InternetSharing, который препятствует переходу Macbook в спящий режим или вновь пробуждает ноутбук, если вручную всё же его усыпить.
Работаю на Macbook, в помещении 3G отсутствует. При подключении ноутбука к сети через RJ-45, удобно использовать InternetSharing (далее IS), так как некоторая работа связанна с мобильными устройствами. Однажды настроив IS забыл о проблеме с интернетом на телефоне, однако стал замечать, когда прихожу на работу, ноутбук включен. Первый раз не придал значения. На второй раз решил посмотреть в логах, когда он же он включается. Оказалось, что включается он спустя пару минут, как я его перевожу в спящий режим.
Как оказалось службой IS блокирует сон в то время как он активен.
Команда
pid 70000: [0x000…000] PreventSystemSleep named: «com.apple.InternetSharing».
Я стал искать как люди решают подобную проблему, но натыкался только на «отключите службу».
Пришлось решать проблему самому.
Как оказалось – настроенная служба IS запускается и останавливается, используя файл:
Решение оказалось таким: включать службу при пробуждении и выключать при переходе в режим сна, то есть использовать такие команды:
Но для этих операций требуется ввод пароля пользователя с правами администратора.
Осталось узнать «Как запускать программы при пробуждении и при переходе в сон» и при этом не требовали бы ввод пароля.
Первое – сделаем свою службу, которая будет запускаться с правами администратора – то есть не требовать ввод пароля для launchctl.
Второе – будет отправлять нашей службе команды при пробуждении и переходе в сон, соответственно start и stop.
Итак, служба будет прослушивать порт, например, 10001 и ждать команды start и stop.
Напишем службу на PHP (полный код ниже), соответственно для запуска IS и его остановки будут производиться следующие действия:
для запуска:
для остановки:
Для запуска нашей службы надо поместить файл
Запустить командой:
Для того чтобы отловить события пробуждения и переход в сон воспользуемся программой
Скрипт запуска
Скрипт остановки
Поместим их в специальные директории программы Scenario:
При включении программа Scenario запускает скрипт из папки "
При переходе в режим сна выполняется аналогичная операция, только с папкой "
Предистория
Работаю на Macbook, в помещении 3G отсутствует. При подключении ноутбука к сети через RJ-45, удобно использовать InternetSharing (далее IS), так как некоторая работа связанна с мобильными устройствами. Однажды настроив IS забыл о проблеме с интернетом на телефоне, однако стал замечать, когда прихожу на работу, ноутбук включен. Первый раз не придал значения. На второй раз решил посмотреть в логах, когда он же он включается. Оказалось, что включается он спустя пару минут, как я его перевожу в спящий режим.
Как оказалось службой IS блокирует сон в то время как он активен.
Команда
укажет, что виновен именно IS:pmset -g assertions
pid 70000: [0x000…000] PreventSystemSleep named: «com.apple.InternetSharing».
Я стал искать как люди решают подобную проблему, но натыкался только на «отключите службу».
Пришлось решать проблему самому.
Что же делать?
Как оказалось – настроенная служба IS запускается и останавливается, используя файл:
/System/Library/LaunchDaemons/com.apple.InternetSharing.plistРешение оказалось таким: включать службу при пробуждении и выключать при переходе в режим сна, то есть использовать такие команды:
- для включения
sudo launchctl load -F /System/Library/LaunchDaemons/com.apple.InternetSharing.plist
Флаг -F обозначает форсированный старт, так как служба имеет флагDisabled <true/>; - для выключения
.sudo launchctl unload /System/Library/LaunchDaemons/com.apple.InternetSharing.plist
Но для этих операций требуется ввод пароля пользователя с правами администратора.
Осталось узнать «Как запускать программы при пробуждении и при переходе в сон» и при этом не требовали бы ввод пароля.
Решение
Первое – сделаем свою службу, которая будет запускаться с правами администратора – то есть не требовать ввод пароля для launchctl.
Второе – будет отправлять нашей службе команды при пробуждении и переходе в сон, соответственно start и stop.
Итак, служба будет прослушивать порт, например, 10001 и ждать команды start и stop.
Напишем службу на PHP (полный код ниже), соответственно для запуска IS и его остановки будут производиться следующие действия:
для запуска:
shell_exec('launchctl load -F /System/Library/LaunchDaemons/com.apple.InternetSharing.plist');
для остановки:
shell_exec('launchctl unload /System/Library/LaunchDaemons/com.apple.InternetSharing.plist');
Для запуска нашей службы надо поместить файл
com.username.InternetSharing.plist в папку /Library/LaunchDaemons/.Запустить командой:
sudo launchctl load -F /Library/LaunchDaemons/com.username.InternetSharing.plist
Для того чтобы отловить события пробуждения и переход в сон воспользуемся программой
Scenario. Напишем для неё на AppleScripts два скрипа, которые будут вызывать клиентскую программу(полный код ниже) с параметром start или stop.Скрипт запуска
do shell script "/usr/bin/php /Users/username/CheckMac/client.php start"Скрипт остановки
do shell script "/usr/bin/php /Users/username/CheckMac/client.php stop"Поместим их в специальные директории программы Scenario:
"~/Library/Scenario/Wake Scripts" и "~/Library/Scenario/Sleep Scripts".Результат
При включении программа Scenario запускает скрипт из папки "
Wake Scripts", который запускает Клиентскую программу с параметром start. Клиентская программа связывается с нашей службой, которая может запустить службу IS, так как обладает достаточными правами.При переходе в режим сна выполняется аналогичная операция, только с папкой "
Sleep Scripts" и с параметром stop.Код
Служба
/Users/username/CheckMac/listen.php
<?php // PHP 5.3.10 // Исходные настройки error_reporting(E_ALL ^ E_WARNING); set_time_limit(0); ob_implicit_flush(); $port = 10001; $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($sock, "127.0.0.1", $port) or die('Port listened'."\n"); socket_set_option($sock, SOL_SOCKET, SO_KEEPALIVE, 1); socket_set_nonblock($sock); socket_listen($sock, 1000); $clients = array($sock); $tarr = array(); $iparr = array(); $current = 0; while (true) { $read = $clients; if (count($tarr) > 0) foreach ($tarr as $ind => $tim) { // Если подключение не проявляет активность 10 секунд - отключать if ((time() - $tim) > 10) { socket_close($read[$ind + 1]); unset($clients[$ind+1]); unset($iparr[$ind]); unset($tarr[$ind]); echo "Disconnect client.\n"; continue; } } // Если нет клиентов - ждать if (socket_select($read, $write = NULL, $except = NULL, 1) < 1) continue; // Новое подключение if (in_array($sock, $read)) { $current ++; $clients[$current] = $newsock = socket_accept($sock); socket_write($newsock, "<OK>\n"); socket_getpeername($newsock, $ip); echo "New connection from ip: {$ip}\n"; $key = array_search($sock, $read); $iparr[$current-1] = $ip; $tarr[$current-1] = time(); unset($read[$key]); $read[$current] = $newsock; continue; } // Проверить подключения foreach ($read as $index => $read_sock) { $data = socket_read($read_sock, 1024); if ($data === false) { $key = array_search($read_sock, $clients); unset($clients[$key]); unset($iparr[$key - 1]); echo "Disconnect client.\n"; unset($tarr[$key - 1]); continue; } $data = trim($data); // Команды для обработки if (!empty($data)) { echo $iparr[$index - 1] . "[$index] - $data\n"; $tarr[$index - 1] = time(); switch ($data) { case "quit": socket_close($read_sock); $key = array_search($read_sock, $clients); unset($clients[$key]); unset($iparr[$key - 1]); echo "Disconnect client.\n"; unset($tarr[$key - 1]); break; case "ping": socket_write($read_sock, "<PONG> " . time() . "\n"); break; // Получили команду на запуск IS case 'start': $s = shell_exec('launchctl list'); if(strpos($s, 'com.apple.InternetSharing') === false){ shell_exec('launchctl load -F /System/Library/LaunchDaemons/com.apple.InternetSharing.plist'); } break; // Получили команду на останов IS case 'stop': $s = shell_exec('launchctl list'); if(strpos($s, 'com.apple.InternetSharing') !== false){ shell_exec('launchctl unload /System/Library/LaunchDaemons/com.apple.InternetSharing.plist'); } break; } if ($data === "close") { socket_close($sock); break(2); } } } } socket_close($sock);
com.username.InternetSharing.plist
/Library/LaunchDaemons/com.username.InternetSharing.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.username.InternetSharing</string> <key>ProgramArguments</key> <array> <string>/usr/bin/php</string> <string>/Users/username/CheckMac/listen.php</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>
Клиентская программа
/Users/username/CheckMac/client.php
<?php $fp = fsockopen("127.0.0.1", 10001, $errno, $errstr, 30); if (!$fp) { echo "$errstr ($errno)<br />\n"; } else { $out = @$argv[1]."\r\n"; fwrite($fp, $out); fgets($fp, 128); sleep(1); // Иначе команды воспринимаются как единый текст $out = "quit\r\n"; fwrite($fp, $out); fgets($fp, 128); fclose($fp); }
Скрипт запуска IS (Не текстовой файл – создаётся программой AppleScripts)
~/Library/Scenario/Wake Scripts/wake.scpt
do shell script "/usr/bin/php /Users/username/CheckMac/client.php start"Скрипт оастнова IS
~/Library/Scenario/Sleep Scripts/sleep.scpt
do shell script "/usr/bin/php /Users/username/CheckMac/client.php stop"