OS Daemonology: види, переваги, підводні камені
Демони, агенти, хелпери — та хто вони такі?!
Мене звати Володимир. Я займаюся macOS-розробкою вже близько 6 років. За цей час працював «від і до» — від дизайну віконець і кастомних кнопок до системного програмування, секьюріті і написання Kernel-модулів.
У цій статті я хочу детально розповісти про «системні» і допоміжних процесах в macOS, які, між іншим, може використовувати у своїй практиці кожен.
Стаття буде корисна будь технічно спрямованих на фахівцям; всім, хто хоче розуміти, як працюють програми і сама ОС (причому не тільки macOS, але й інші *OS) «під капотом»; і звичайно, тим, хто хоче знати, як і коли використовувати тих чи інших демонів при розробці своїх програм.
Що ми бачимо і в чому правда?
Як звичайний користувач бачить операційну систему зі свого боку?
Просто як набір віконних додатків з барвистим дизайном і приємною анімацією.
При цьому очевидно, що для роботи цих програм, так і всієї системи повинно бути щось ще.
Демони всередині!
Демони?
У розмовній мові зазвичай можна почути термін «демон», що хоч і неправильно, але використовується повсюдно. Варто пам'ятати, що Daemon != Demon. Згідно історичній довідці, daemon — це якесь створення, породжене богами для виконання різного роду робіт, якими самі боги займатися не хочуть. Ця термінологія вперше була використана в так званому демона Максвелла .
Так хто ж такі всі ці daemons і навіщо вони потрібні?
Це в першу чергу OS support, а також моніторинг, функції серверної частини додатків, task managers. Вони виконуються у background і (зазвичай) позбавлені будь-якого GUI.
Сам daemon в системі операційних систем *OS представлений конфіг (.plist) з описом демона плюс безпосередньо виконуваним файлом.
Конфігураційний файл .plist описує і визначає поведінку демона.
Наприклад:
- Label — ідентифікатор демона:
com.example.mydaemond - ProgramArguments — аргументи командного рядка, які використовуються для старту демона:
/bin/rm-rf, "/tmp/trashfiles"
/Library/Application Support/CoolApp/com.coolapp.monitord
Ще є велика кількість інших аргументів, таких як:
- KeepAlive (launchd перезавантажить процес, якщо процес буде завершено);
- RunAtLoad (launchd запустить процес на старті (системи/логін-сесії));
- WorkingDirectory (робоча директорія процесу);
- WatchPaths (шляхи файлової системи, за якими буде стежити демон).
Note: ви можете детальніше прочитати про ці та інші атрибути файлу конфігурації в мануалі до launchd:man launchd.plist.
Також можна звернутися до офіційної статті Apple .
launchd — самий перший демон
В *OS-системах є «особливий» демон, ім'я якому — launchd.
launchd — це аналог init UNIX-подібних систем. launchd завжди має pid 1 (pid 0 — це сам Kernel).
Основні завдання launchd:
- ініціалізація системи;
- запуск/перезапуск процесів;
- підтримка демонів;
- підтримка XPC.
Цікавий факт: у launchd є персональний .
*OS Daemonology в деталях
Note: це опис наводиться на прикладі macOS як системи з відкритим доступом до файлової системи. При цьому пристрій інших ОС сімейства Apple iOS, watchOS...) аналогічне.
Apple пропонує класифікувати всіх демонів по наступним категоріям:
- XPC Service ;
- UserAgent ;
- Daemon ;
- PrivilegedHelper ;
- LoginItem .
XPC Service
Мабуть, XPC Service — це самий пропиаренный і задокументований вид демонів в macOS.
Apple самі рекомендують використовувати XPC Service в додатках. Їх навіть можна вбудовувати у фреймворки.
Основні факти про XPC Service:
- App Store friendly;
- спеціальна мета в Xcode;
- працює як поточний користувач;
- GUI-less;
- stateless (може бути знищений при запуску на IDLE);
- відноситься до конкретного додатком;
- живе не довше, ніж батьківське додаток;
- може бути встановлений в ізольованому додатку.
Резюмуючи, можна сказати, що XPC Service дозволяє винести частину коду логіки в окремий процес, при цьому надаючи зручний механізм взаємодії між процесами «з коробки».
XPC Service є досить обмеженим процесом, сильно пов'язаним зі своїм батьком. Наприклад, якщо два різних додатки вмонтують в себе один і той же XPC Service, то для кожної програми буде запущений окремий і незалежний примірник сервісу.
Важливі замітки про XPC Service:
- не може бути користувачем root;
- не може використовувати які-небудь функції графічного інтерфейсу користувача (Windows, повідомлення, ...);
- може бути non-sandboxed, навіть якщо основне додаток (звичайно, не в AppStore).
UserAgent
Досить популярною різновидом демонів є UserAgent. Він являє собою самостійний background-процес, запущений від поточного користувача. Зазвичай існує 1 процес UserAgent для кожного конкретного користувача системи (для кожної логін-сесії).
Основні факти про UserAgent:
- незалежне додаток;
- працює як поточний користувач;
- може мати стан;
- може відображати графічний інтерфейс (вікна, повідомлення тощо).
UserAgent зручно використовувати для винесення в нього всієї бізнес-логіки додатка, стану і функціоналу, щоб в основному додатку залишити тільки UI.
Також з допомогою UserAgent можна координувати роботу різних процесів — компонентів додатка.
Note: встановити UserAgent в систему можна тільки з НЕ-Sandbox-додатки.
Vanilla daemon
Daemon як daemon (в класичному розумінні) — це системно-глобальний процес, запущений від root. Daemon існує один для всієї системи, а також може стартувати в момент завантаження ОС і до процедури аутентифікації користувача.
Демонів зазвичай використовують в тих випадках, коли потрібна одна або кілька особливостей:
- виконання привілейованих операцій (від root);
- зберігання глобального стану незалежно від логін-сесій;
- координація безлічі UserAgent;
- установка KEXT.
Основні факти про Daemon:
- незалежне додаток;
- запускається як root;
- може мати стан;
- може запускатися при завантаженні OS;
- singleton;
- GUI-less.
Note: Apple НЕ рекомендує використовувати демонів, пропонуючи замінити їх UserAgent або в крайньому випадку PrivilegedHelper з міркувань безпеки. Якщо слідувати цій рекомендації, то ризик компрометації даних користувача і системи в цілому знижується.
PrivilegedHelper
PrivilegedHelper можна назвати «урізаним» демоном.
В Apple говорять, що якщо вже просто несила використовувати root, то тоді хоча б використовуйте PrivilegedHelper замість звичайних скриптів від root.
По своїм властивостям це такий же Daemon, але при цьому існує ряд особливостей:
- повинен бути встановлений виключно з допомогою API SMJobBless ;
- при установці копіюється в /Library/PrivilegedHelperTools.
Здавалося б, що такого в копіюванні в /Library/PrivilegedHelperTools?
А те, що відламується будь-яка динамічна лінковка зі своїми бібліотеками та фреймворками, оскільки процес не знає, де їх шукати.
Для вирішення питання можна йти різними шляхами:
- Статичні бібліотеки.
- Runtime-лінкування.
Основні факти про PrivilegedHelper:
- «рекомендований» демон;
- встановлюється через SMJobBless ;
- зазвичай використовується як установник/дєїнсталлятор;
- керівництво по використанню .
Note: PrivilegedHelper НЕ може бути встановлений Sandboxed-додатки .
LoginItem
LoginItem є сумісним з App Store (і урізаним) варіантом UserAgent. По суті, це звичайний UserAgent, який встановлюється за допомогою легального API, що надається Apple.
Основні факти про LoginItem:
- користувальницький агент з «обмеженим» доступом;;
- встановлюється через SMLoginItemSetEnabled ;
- може бути встановлений в ізольованому додатку;
- автоматично запускається при вході користувача в систему;
- керівництво по використанню.
Note: це НЕ той LoginItem, який можна побачити в Settings.
Де живуть демони?
Як говорилося раніше, демон — це пара з файлу конфігурації і самої виконуваної програми. Де розмістити сам виконуваний файл, не має зовсім ніякого значення, так як це просто шлях в секції Program/ProgramArguments.
Файли конфігурації розміщуються: Шлях | Приклад |
Daemons: /Library/LaunchDaemons |
/Library/LaunchDaemons /System/Library/LaunchDaemons |
Global (all users) Agents: /Library/LaunchAgents |
/Library/LaunchAgents /System/Library/LaunchAgents |
User-specific Agents: ~/Library/LaunchAgents |
/Users/JohnDoe/LaunchAgents /Users/JaneDoe/LaunchAgents ... ~/LaunchAgents |
Відмінність глобальних UserAgent від user-specific в тому, що глобальні агенти будуть асоційовані з усіма користувачами ОС. При цьому user-specific-агенти асоційовані тільки з конкретним користувачем.
Note: щоб помістити агента в «глобальні», потрібні права root.
Переваги демонів
Ми розглянули основні актуальні на сьогоднішній день види демонів в сімействі систем *OS. Часто задається питання — «Навіщо мені XPC Service, якщо я можу просто запустити ще один потік?».
Існують наступні переваги використання демонів в різних їх проявах.
EXC_BAD_ACCESS, crash-resistance
Звичайно, всі ми пишемо ідеальний код. Зрозуміло, він ніколи не крашится і не засмучує цим наших користувачів. Давайте зробимо припущення, що десь ми все ж пропустили якийсь дефект, що приводить до падіння програми.
У випадку з «класичним» монолітним підходом до додатка користувач втрачає все і відразу. Однак якщо винести частини коду з окремих сервісів, то найгірше, що нас чекає, — це втрата тільки однієї фічі (при цьому з показом нефатальной помилки). Звичайно, такий підхід дещо ускладнює архітектуру системи. Але що таке невелика складність перед такими величезними перевагами, як crash safety?
Security
Та ні, не сесюрити. Розміщення свого коду в демонів сильно ускладнює завдання зловмисникам і просто реверс-інженерам, яким з якоїсь причини захотілося розібратися в тому, як працює ваш код. Код, який знаходиться у фреймворку, вкрай легко експлуатувати і вивчати шляхом динамічного аналізу (простими словами, дебагом). Код, який «вшитий» в демона, вже так легко не подебажишь, а також не використовуєш безпосередньо. Як мінімум поки захист системи не впала, а також поки у anonymous немає прав root.
Обмежене використання root
Виносячи код, що вимагає root, в окремий демон, йдемо за принципом «не використовуй зайве без необхідності». Цей принцип пронизує також ідеологію, яку просуває Apple, яка говорить: «Використовуй модулі Kernel тільки в тому випадку, коли не можеш обійтися привілейованим процесом. Використовуй привілейовані процеси тільки для тих операцій, які дійсно цього потребують». Виділяючи самий мінімум функціоналу в рутові демони, ми забезпечуємо захист не тільки своїх додатків, але і всієї системи і даних користувача на той випадок, якщо в нашому демона знайдеться хоча б маленька дірочка.
Особисті демони
Ми розглянули безліч різних видів демонів. Далі я пропоную просту блок-схему, дотримуючись якої можна легко вибрати демона саме під ваші потреби.
При виборі між XPC Service, UserAgent і LoginItem пропоную керуватися наступними міркуваннями і практичним досвідом.
XPC Service | LoginItem | UserAgent |
|
|
|
Контроль демонів — launchctl
Якщо XPC Services, LoginItem, PrivilegedHelper будуть (повинні) працювати «з коробки», то, щоб завести повноцінного UserAgent або Daemon, доведеться вже працювати з системними тулзами.
macOS надає тулзу launchctl , за допомогою якої можна:
- отримати список всіх «демонічних» проявів, зареєстрованих для кожного користувача;
- завантажити/завантажити демона;
- увімкнути/вимкнути демона і т. д.
Examples
List agents for current user: launchctl list Load agent: launchctl bootstrap gui/501 /Library/LaunchAgents/com.company.launchagent.plist Unload agent: launchctl bootout gui/501 /Library/LaunchAgents/com.company.launchagent.plist where <user's UID> is user id (uid_t) List daemons: sudo launchctl list Load daemon: sudo launchctl bootstrap system /Library/LaunchDaemons/com.company.launchdaemon.plist Unload daemon: sudo launchctl bootout system /Library/LaunchDaemons/com.company.launchdaemon.plist
Більш повна документація по launchctl знаходиться за посиланнями: Launchctl 2.0 syntax і cheatsheet.md .
Це тільки початок
Спочатку я планував розбити цю тему на дві статті: одну — про різновиди демонів в *OS плюс опис XPC як механізму межпроцессного взаємодії, другу — про low-level-реалізації XPC. Проте, як зазвичай, матеріалу вийшло більше, ніж передбачалося. Тому про XPC мова буде йти в окремій статті.
Підписуйтесь і стежте за цікавими статтями та code sample: GitHub і .
Опубліковано: 29/10/19 @ 11:00
Розділ Безпека
Рекомендуємо:
Криза перевиробництва джунов
AI & ML дайджест #15: вибір ML-фреймворку, вивчаємо TensorFlow 2.0 + Keras, шлях навчання Data Science
Як ми розробили Android-застосунок і втратили все, крім досвіду
Інтеграційні платформи (iPaaS): у чому фішка
Кремнієва долина Китаю. Як програмісту живеться у Шеньчжені