файл launchd.plist работает каждые 10 секунд вместо одного раза
Я настраиваю launchd.plist
XML, который запускается каждый раз, когда монтируется конкретное USB-устройство. Я следовал инструкциям на man-страницеxpc_events(3) , и она запускает приложение всякий раз, когда устройство монтируется.
Проблема, с которой я сталкиваюсь, заключается в том, что приложение запускается снова и снова каждые 10 секунд, пока устройство все еще подключено. Как настроить его так, чтобы он работал только один раз, когда устройство вставлено в USB-порт?
<?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.myapp.agent</string>
<key>Program</key>
<string>/Applications/MyApp.app/Contents/MacOS/MyAgent</string>
<key>LaunchEvents</key>
<dict>
<key>com.apple.iokit.matching</key>
<dict>
<key>com.apple.device-attach</key>
<dict>
<key>idVendor</key>
<integer>2316</integer>
<key>idProduct</key>
<integer>4096</integer>
<key>IOProviderClass</key>
<string>IOUSBDevice</string>
<key>IOMatchLaunchStream</key>
<true/>
</dict>
</dict>
<key>com.apple.notifyd.matching</key>
<dict>
<key>com.apple.interesting-notification</key>
<dict>
<key>Notification</key>
<string>com.apple.interesting-notification</string>
</dict>
</dict>
</dict>
</dict>
</plist>
4 ответа:
AIUI ваше приложение должно вызвать xpc_set_event_stream_handler, чтобы удалить событие из очереди. Возможно, Вам также придется добавить
<key>KeepAlive</key><false/>
.плист, но я в этом не уверен.
Я пытаюсь использовать что-то вроде этого:
#include <xpc/xpc.h> #include <unistd.h> #include <asl.h> int main(int argc, char *argv[]) { if (argc < 2) { return 1; } asl_log(NULL, NULL, ASL_LEVEL_DEBUG, "event_stream_handler: starting"); xpc_set_event_stream_handler("com.apple.iokit.matching", dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(xpc_object_t event) { const char *name = xpc_dictionary_get_string(event, XPC_EVENT_KEY_NAME); uint64_t id = xpc_dictionary_get_uint64(event, "IOMatchLaunchServiceID"); asl_log(NULL, NULL, ASL_LEVEL_DEBUG, "event_stream_handler: received event: %s: %llu", name, id); execv(argv[1], argv + 1); }); dispatch_main(); return 0; }
Таким образом, сценарий, который потребляет событие и запускает сценарий, переданный в качестве аргумента.
Я написал учебник по этому с подробными инструкциями и файлами примеров для запуска произвольного исполняемого файла или скрипта оболочки путем подключения внешнего устройства (usb/thunderbolt) к компьютеру Mac, без проблемы респауна.
Как и подход авторов, он опирается на библиотеку AppleIOKit
для обнаружения устройств и демона для запуска желаемого исполняемого файла. Чтобы демон не запускался повторно после подключения устройства, специальный поток обработчик (xpc_set_event_stream_handler
) используется для "потребления" событияcom.apple.iokit.matching
, как описано в посте @ford и в его РЕПО github.В частности, учебник описывает, как скомпилировать обработчик потока xpc и как ссылаться на него вместе с исполняемым файлом в файле daemon plist и где разместить все соответствующие файлы с правильными разрешениями.
Для файлов, пожалуйста, перейдите сюда . Для полноты картины я также вставил их содержание ниже.
Беги сценарий оболочки или исполняемый файл, вызванный обнаружением устройства на mac
Здесь я использую пример подмены MAC-адреса адаптера ethernet, когда он подключен к Mac. Это можно обобщить на произвольные исполняемые файлы и устройства.
Поместите свой сценарий оболочки или исполняемый файл на место
Адаптировать сценарий оболочки
spoof-mac.sh
#!/bin/bash ifconfig en12 ether 12:34:56:78:9A:BC
К вашим потребностям и сделайте его исполняемым:
sudo chmod 755 spoof-mac.sh
Затем переместите его в
/usr/local/bin
или в другой каталог:Cp spoof-mac.sh /usr / local / bin /
Построение обработчика потока
Обработчик потока
xpc_set_event_stream_handler.m
// Created by Ford Parsons on 10/23/17. // Copyright © 2017 Ford Parsons. All rights reserved. // #import <Foundation/Foundation.h> #include <xpc/xpc.h> int main(int argc, const char * argv[]) { @autoreleasepool { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); xpc_set_event_stream_handler("com.apple.iokit.matching", NULL, ^(xpc_object_t _Nonnull object) { const char *event = xpc_dictionary_get_string(object, XPC_EVENT_KEY_NAME); NSLog(@"%s", event); dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); if(argc >= 2) { execv(argv[1], (char **)argv+1); } } }
Является универсальным (нет необходимости адаптироваться) и может быть построен на командной строке mac (с установленным xcode):
gcc -framework Foundation -o xpc_set_event_stream_handler xpc_set_event_stream_handler.m
Давайте поместим его в
/usr/local/bin
, как основной исполняемый файл для демона.cp xpc_set_event_stream_handler /usr/local/bin/
Настройка демона
Файл plist
com.spoofmac.plist
содержит свойства демона, который будет запускать исполняемый файл на устройстве connect спусковой крючок.<?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>UserName</key> <string>root</string> <key>StandardErrorPath</key> <string>/tmp/spoofmac.stderr</string> <key>StandardOutPath</key> <string>/tmp/spoofmac.stdout</string> <key>Label</key> <string>com.spoofmac.program</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/xpc_set_event_stream_handler</string> <string>/usr/local/bin/spoofmac.sh</string> </array> <key>LaunchEvents</key> <dict> <key>com.apple.iokit.matching</key> <dict> <key>com.apple.device-attach</key> <dict> <key>idVendor</key> <integer>32902</integer> <key>idProduct</key> <integer>5427</integer> <key>IOProviderClass</key> <string>IOPCIDevice</string> <key>IOMatchLaunchStream</key> <true/> <key>IOMatchStream</key> <true/> </dict> </dict> </dict> </dict> </plist>
Он содержит информацию для идентификации устройства, на котором вы хотите основать свой триггер, например
idVendor
,idProduct
,IOProviderClass
. Они могут быть вычислены в приложенииSystem Information
на вашем mac.Преобразуйте шестнадцатеричные идентификаторы в целые числа перед вставкой в файл plist (например, используя
int(0x8086)
в python).
IOProviderClass
должно быть либоIOPCIDevice
(Thunderbolt), либоIOUSBDevice
(USB).Другой соответствующей записью в файле plist является расположение
xpc_set_event_stream_handler
и исполняемого файла.Другие записи включают расположение стандартных выходных файлов (log) и исполняющего пользователя.
Поскольку подмена MAC требует привилегий root, мы помещаем
com.spoofmac.plist
в/Library/LaunchDaemons
:cp com.spoofmac.plist /Library/LaunchDaemons/
Не в папку
LaunchAgents
. Агенты запуска игнорируют аргументUserName
.Убедитесь, что владельцем файла является
root
:sudo chown root:wheel /Library/LaunchDaemons/com.spoofmac.plist
Запуск демона
Активируйте демона:
launchctl load /Library/LaunchDaemons/com.spoofmac.plist
И ты хорошо относишься к идти.
Разгрузка производится с помощью
launchctl unload
.
Это работает для меня:
int main(int argc, const char * argv[]) { @autoreleasepool { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); xpc_set_event_stream_handler("com.apple.iokit.matching", NULL, ^(xpc_object_t _Nonnull object) { const char *event = xpc_dictionary_get_string(object, XPC_EVENT_KEY_NAME); dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); if(argc >= 2) { execv(argv[1], (char **)argv+1); } } }