OS Daemonology: види, переваги, підводні камені

Демони, агенти, хелпери — та хто вони такі?!

Мене звати Володимир. Я займаюся macOS-розробкою вже близько 6 років. За цей час працював «від і до» — від дизайну віконець і кастомних кнопок до системного програмування, секьюріті і написання Kernel-модулів.

У цій статті я хочу детально розповісти про «системні» і допоміжних процесах в macOS, які, між іншим, може використовувати у своїй практиці кожен.

Стаття буде корисна будь технічно спрямованих на фахівцям; всім, хто хоче розуміти, як працюють програми і сама ОС (причому не тільки macOS, але й інші *OS) «під капотом»; і звичайно, тим, хто хоче знати, як і коли використовувати тих чи інших демонів при розробці своїх програм.

Що ми бачимо і в чому правда?

Як звичайний користувач бачить операційну систему зі свого боку?

Просто як набір віконних додатків з барвистим дизайном і приємною анімацією.

При цьому очевидно, що для роботи цих програм, так і всієї системи повинно бути щось ще.

Демони всередині!

Демони?

У розмовній мові зазвичай можна почути термін «демон», що хоч і неправильно, але використовується повсюдно. Варто пам'ятати, що Daemon != Demon. Згідно історичній довідці, daemon — це якесь створення, породжене богами для виконання різного роду робіт, якими самі боги займатися не хочуть. Ця термінологія вперше була використана в так званому демона Максвелла .

Так хто ж такі всі ці daemons і навіщо вони потрібні?

Це в першу чергу OS support, а також моніторинг, функції серверної частини додатків, task managers. Вони виконуються у background і (зазвичай) позбавлені будь-якого GUI.

Сам daemon в системі операційних систем *OS представлений конфіг (.plist) з описом демона плюс безпосередньо виконуваним файлом.

Конфігураційний файл .plist описує і визначає поведінку демона.

Наприклад:

Ще є велика кількість інших аргументів, таких як:

Note: ви можете детальніше прочитати про ці та інші атрибути файлу конфігурації в мануалі до launchd:man launchd.plist.

Також можна звернутися до офіційної статті Apple .

launchd — самий перший демон

В *OS-системах є «особливий» демон, ім'я якому — launchd.

launchd — це аналог init UNIX-подібних систем. launchd завжди має pid 1 (pid 0 — це сам Kernel).

Основні завдання launchd:

Цікавий факт: у launchd є персональний .

*OS Daemonology в деталях

Note: це опис наводиться на прикладі macOS як системи з відкритим доступом до файлової системи. При цьому пристрій інших ОС сімейства Apple iOS, watchOS...) аналогічне.

Apple пропонує класифікувати всіх демонів по наступним категоріям:

XPC Service

Мабуть, XPC Service — це самий пропиаренный і задокументований вид демонів в macOS.

Apple самі рекомендують використовувати XPC Service в додатках. Їх навіть можна вбудовувати у фреймворки.

Основні факти про XPC Service:

Резюмуючи, можна сказати, що XPC Service дозволяє винести частину коду логіки в окремий процес, при цьому надаючи зручний механізм взаємодії між процесами «з коробки».

XPC Service є досить обмеженим процесом, сильно пов'язаним зі своїм батьком. Наприклад, якщо два різних додатки вмонтують в себе один і той же XPC Service, то для кожної програми буде запущений окремий і незалежний примірник сервісу.

Важливі замітки про XPC Service:

UserAgent

Досить популярною різновидом демонів є UserAgent. Він являє собою самостійний background-процес, запущений від поточного користувача. Зазвичай існує 1 процес UserAgent для кожного конкретного користувача системи (для кожної логін-сесії).

Основні факти про UserAgent:

UserAgent зручно використовувати для винесення в нього всієї бізнес-логіки додатка, стану і функціоналу, щоб в основному додатку залишити тільки UI.

Також з допомогою UserAgent можна координувати роботу різних процесів — компонентів додатка.

Note: встановити UserAgent в систему можна тільки з НЕ-Sandbox-додатки.

Vanilla daemon

Daemon як daemon (в класичному розумінні) — це системно-глобальний процес, запущений від root. Daemon існує один для всієї системи, а також може стартувати в момент завантаження ОС і до процедури аутентифікації користувача.

Демонів зазвичай використовують в тих випадках, коли потрібна одна або кілька особливостей:

Основні факти про Daemon:

Note: Apple НЕ рекомендує використовувати демонів, пропонуючи замінити їх UserAgent або в крайньому випадку PrivilegedHelper з міркувань безпеки. Якщо слідувати цій рекомендації, то ризик компрометації даних користувача і системи в цілому знижується.

PrivilegedHelper

PrivilegedHelper можна назвати «урізаним» демоном.

В Apple говорять, що якщо вже просто несила використовувати root, то тоді хоча б використовуйте PrivilegedHelper замість звичайних скриптів від root.

По своїм властивостям це такий же Daemon, але при цьому існує ряд особливостей:

Здавалося б, що такого в копіюванні в /Library/PrivilegedHelperTools?

А те, що відламується будь-яка динамічна лінковка зі своїми бібліотеками та фреймворками, оскільки процес не знає, де їх шукати.

Для вирішення питання можна йти різними шляхами:

  1. Статичні бібліотеки.
  2. Runtime-лінкування.

Основні факти про PrivilegedHelper:

Note: PrivilegedHelper НЕ може бути встановлений Sandboxed-додатки .

LoginItem

LoginItem є сумісним з App Store (і урізаним) варіантом UserAgent. По суті, це звичайний UserAgent, який встановлюється за допомогою легального API, що надається Apple.

Основні факти про LoginItem:

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

  • AppStore-friendly;
  • завжди упакований;
  • може бути вбудований у фреймворки;
  • немає графічного інтерфейсу;
  • stateless.

  • AppStore-friendly;
  • може мати графічний інтерфейс;
  • завжди упакований;
  • нативна установка API;
  • SMLoginItemSetEnabled;
  • складності при оновленні/перевстановлення;
  • неочевидні способи комунікацій;
  • недокументовану поведінка при запуску/повторному запуску.

  • може мати графічний інтерфейс;
  • пакет або виконуваний файл;
  • гнучкий launchd.plist;
  • процедура установки за допомогою інструменту «launchctl»;
  • для установки потрібно non-sandboxed app.

Контроль демонів — 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): у чому фішка
Кремнієва долина Китаю. Як програмісту живеться у Шеньчжені