Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Intro into Daemons & Agents on macOS

vashchenko
November 12, 2018

Intro into Daemons & Agents on macOS

This presentation describes the ways to add a daemon or an agent to macOS—both using system API (SMJobBless(), SMLoginItemSetEnabled()) and command-line tool launchctl.

vashchenko

November 12, 2018
Tweet

More Decks by vashchenko

Other Decks in Programming

Transcript

  1. Intro into
    Daemons & Agents
    on macOS

    View Slide

  2. Daemon: runs under root user
    Agent: runs under any other user
    Difference between
    Daemon & Agent

    View Slide

  3. Single daemon for root tasks
    +
    multiple per-user agents
    Typical scheme in application

    View Slide

  4. How to add Daemon
    using API (from code) using command-line tools
    deprecated
    deprecated current
    current
    SMJobBless()
    AuthorizationExecute
    WithPrivileges()
    launchctl
    load / unload
    launchctl
    bootstrap / bootout

    View Slide

  5. How to add Daemon
    using API (from code) using command-line tools
    deprecated
    deprecated current
    current
    SMJobBless()
    AuthorizationExecute
    WithPrivileges()
    launchctl
    load / unload
    launchctl
    bootstrap / bootout

    View Slide

  6. How to add Agent
    using API (from code) using command-line tools
    deprecated current
    current
    SMLoginItemSetEnabled() load / unload bootstrap / bootout

    View Slide

  7. How to add Agent
    using API (from code) using command-line tools
    deprecated current
    current
    SMLoginItemSetEnabled() load / unload bootstrap / bootout

    View Slide

  8. How to add Daemon/Agent
    using command-line tool
    1. Add .plist with Daemon/Agent configuration to specified location in system*

    2. If adding Daemon or Agent for all users, set plist's owner to root (sudo chown
    root)

    3. Load this .plist to launchd (this step can be skipped: in that case launchd will load
    .plist from listed locations during the next login. But if you want your job to start
    now, you need to load .plist)
    */Library/LaunchAgents | ~/Library/LaunchAgents for agent
    * /Library/LaunchDaemons for daemon
    for more details on plist configuration files see next slides of this presentation and man launchd.plist

    View Slide

  9. How to add Daemon/Agent
    using system API
    1. Use SMJobBless() to load Daemon or SMLoginItemSetEnabled() to load Agent.
    2. Remember to follow additional requirements for given API to work properly.
    for more details see next slides of this presentation

    View Slide

  10. Using command-line tools

    View Slide

  11. How to add Daemon/Agent
    Loading configuration .plist to launchd
    launchd is waaaaaaaaaaaaay too cool, so you don't speak to him directly

    View Slide

  12. How to add Daemon/Agent
    Loading configuration .plist to launchd
    but he has a secretary — Launch Control, launchctl for short
    launchd is waaaaaaaaaaaaay too cool, so you don't speak to him directly

    View Slide

  13. Add:
    * Daemon:
    sudo launchctl load /Library/LaunchDaemons/com.mycompany.mydaemon.plist
    * Agents for all users:
    sudo launchctl load /Library/LaunchAgents/com.mycompany.myagent.plist
    * Agent for one user:
    launchctl load ~/Library/LaunchAgents/com.mycompany.myagent.plist
    Remove:
    * Daemon:
    sudo launchctl unload /Library/LaunchDaemons/com.mycompany.mydaemon.plist
    * Agents for all users:
    sudo launchctl unload /Library/LaunchAgents/com.mycompany.myagent.plist
    * Agent for one user:
    launchctl unload ~/Library/LaunchAgents/com.mycompany.myagent.plist
    How to load Daemon/Agent
    .plist to launchd
    Command-line API before 10.11

    View Slide

  14. How to load Daemon/Agent
    .plist to launchd
    Command-line API before 10.11
    Add:
    * Daemon:
    sudo launchctl load /Library/LaunchDaemons/com.mycompany.mydaemon.plist
    * Agents for all users:
    sudo launchctl load /Library/LaunchAgents/com.mycompany.myagent.plist
    * Agent for one user:
    launchctl load ~/Library/LaunchAgents/com.mycompany.myagent.plist
    Remove:
    * Daemon:
    sudo launchctl unload /Library/LaunchDaemons/com.mycompany.mydaemon.plist
    * Agents for all users:
    sudo launchctl unload /Library/LaunchAgents/com.mycompany.myagent.plist
    * Agent for one user:
    launchctl unload ~/Library/LaunchAgents/com.mycompany.myagent.plist

    View Slide

  15. List all Daemons/Agents,
    submitted to launchd
    sudo launchctl list ← print jobs, running as root
    launchctl list ← print jobs, running as other user

    View Slide

  16. How to load Daemon/Agent
    .plist to launchd
    Command-line API since 10.11
    Add:
    * Daemon:
    sudo launchctl bootstrap system /Library/LaunchDaemons/com.mycompany.mydaemon.plist
    'system' is the 'domain' of the submitted executable.
    See man launchctl for details about domains

    View Slide

  17. How to load Daemon/Agent
    .plist to launchd
    Command-line API since 10.11
    Add:
    * Daemon:
    sudo launchctl bootstrap system /Library/LaunchDaemons/com.mycompany.mydaemon.plist
    * Agents for all users:
    sudo launchctl bootstrap gui/501 /Library/LaunchAgents/com.mycompany.myagent.plist
    Domain for user agent is 'gui/[user id]'.
    To register agent for all users you should call this command
    with every user id as given user (not as root).

    View Slide

  18. How to load Daemon/Agent
    .plist to launchd
    Command-line API since 10.11
    Add:
    * Daemon:
    sudo launchctl bootstrap system /Library/LaunchDaemons/com.mycompany.mydaemon.plist
    * Agents for all users:
    sudo launchctl bootstrap gui/501 /Library/LaunchAgents/com.mycompany.myagent.plist
    * Agent for one user:
    launchctl bootstrap gui/501 ~/Library/LaunchAgents/com.mycompany.myagent.plist
    Remove:
    * Daemon:
    sudo launchctl bootout system /Library/LaunchDaemons/com.mycompany.mydaemon.plist
    * Agents for all users:
    sudo launchctl bootout gui/501 /Library/LaunchAgents/com.mycompany.myagent.plist
    * Agent for one user:
    launchctl bootout gui/501 ~/Library/LaunchAgents/com.mycompany.myagent.plist

    View Slide

  19. sudo launchctl print system
    launchctl print gui/501
    List all Daemons/Agents,
    that launchd knows about
    List all agents, registered for user
    with given id (replace 501 with relevant id)
    List all daemons

    View Slide

  20. Command-line API since 10.11
    sudo launchctl disable system/com.mycompany.mydaemon
    sudo launchctl disable gui/501/com.myagent
    launchctl disable gui/501/com.myagent
    Disable
    Enable
    sudo launchctl enable system/com.mycompany.mydaemon
    sudo launchctl enable gui/501/com.myagent
    launchctl enable gui/501/com.myagent
    Switch on/off
    your Daemon/Agent

    View Slide

  21. Using system API

    View Slide

  22. How to add Daemon/Agent
    Framework: Security
    AuthorizationExecuteWithPrivileges(...)
    from code before 10.7

    View Slide

  23. How to add Daemon/Agent
    Framework: Security
    AuthorizationExecuteWithPrivileges(...)
    from code before 10.7

    View Slide

  24. Framework: Service Management
    Enable a user agent application,
    located in the main application
    bundle’s

    “Contents/Library/LoginItems”
    directory.

    Compatible with AppStore
    Enable a daemon.

    Replaces

    AuthorizationExecuteWithPrivileges()
    NOT compatible with AppStore
    How to add Daemon/Agent
    from code after 10.7

    View Slide

  25. Requirements to adding
    Daemon via SMJobBless()
    1. The calling application and target executable tool must both be signed.
    2. The calling application's Info.plist must include a "SMPrivilegedExecutables" dictionary of strings.
    Each string is a textual representation of a code signing requirement used to determine whether the
    application owns the privileged tool once installed (i.e. in order for subsequent versions to update the
    installed version). See example on next slide.
    3. The helper tool must have an embedded Info.plist containing an "SMAuthorizedClients" array of
    strings. Each string is a textual representation of a code signing requirement describing a client which
    is allowed to add and remove the tool. How to embed: see futher.
    4. The helper tool must have an embedded launchd plist. The only required key in this plist is the
    Label key. When the launchd plist is extracted and written to disk, the key for ProgramArguments will
    be set to an array of 1 element pointing to a standard location. You cannot specify your own program
    arguments, so do not rely on custom command line arguments being passed to your tool. Pass any
    parameters via IPC.
    5. The helper tool must reside in the Contents/Library/LaunchServices directory inside the
    application bundle, and its name must be its launchd job label. So if your launchd job label is
    "com.apple.Mail.helper", this must be the name of the tool in your application bundle.
    Details: https://developer.apple.com/documentation/servicemanagement/1431078-smjobbless

    View Slide

  26. How to add Daemon
    SMPrivilegedExecutables key in client app's Info.plist
    key
    signing requirement to owned tool (see example on next slide);
    there can be multiple tools owned by same app

    View Slide

  27. How to add Daemon
    Signing requirements example
    See Code Signing Requirement Language for syntax

    View Slide

  28. How to add Daemon
    SMAuthorizedClients key in Info.plist of the Daemon
    key
    signing requirements to client applications
    Note: you can additionally use here other keys, listed in man launchd.plist

    View Slide

  29. How to embed plists in
    Daemon for SMJobBless()
    path to .plist
    path to .plist
    Add 'Other linker flags', shown on the screenshot:

    View Slide

  30. Using SMJobBless(): Flow
    1. Launch the client application (that owns the Daemon)
    2. Obtain SFAutorization object (for kAuthorizationRightExecute)
    3. Call SMJobBless with required arguments:
    — domain kSMDomainSystemLaunchd
    — label of the Daemon
    — AuthorizationRef from obtained authorization
    — reference to NSError object
    That's it: now you can talk to your Daemon via XPC to ask it to do
    its job.

    View Slide

  31. How to add Agent via
    SMLoginItemSetEnabled()
    1. The helper tool must reside in the Contents/Library/LoginItems directory inside the
    application bundle.
    Details: https://developer.apple.com/documentation/servicemanagement/1501557-smloginitemsetenabled

    View Slide

  32. Requirements to adding Agent
    via SMLoginItemSetEnabled()
    1. The helper tool must reside in the Contents/Library/LoginItems directory inside the
    application bundle.
    Details: https://developer.apple.com/documentation/servicemanagement/1501557-smloginitemsetenabled
    Yes, that's it.

    View Slide

  33. Using
    SMLoginItemSetEnabled(): Flow
    1. Launch the client application (that owns the Agent)
    2. Call SMLoginItemSetEnabled() with required arguments:
    — agent's bundle identifier
    — `true` or `false` depending if you want to run it right now

    View Slide

  34. Thank you!
    Happy to help via [email protected]

    View Slide