Intro into Daemons & Agents on macOS

6e39a16132f9e41e6869527ba0ced276?s=47 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.

6e39a16132f9e41e6869527ba0ced276?s=128

vashchenko

November 12, 2018
Tweet

Transcript

  1. Intro into Daemons & Agents on macOS

  2. Daemon: runs under root user Agent: runs under any other

    user Difference between Daemon & Agent
  3. Single daemon for root tasks + multiple per-user agents Typical

    scheme in application
  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
  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
  6. How to add Agent using API (from code) using command-line

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

    tools deprecated current current SMLoginItemSetEnabled() load / unload bootstrap / bootout
  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
  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
  10. Using command-line tools

  11. How to add Daemon/Agent Loading configuration .plist to launchd launchd

    is waaaaaaaaaaaaay too cool, so you don't speak to him directly
  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
  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
  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
  15. List all Daemons/Agents, submitted to launchd sudo launchctl list ←

    print jobs, running as root launchctl list ← print jobs, running as other user
  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
  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).
  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
  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
  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
  21. Using system API

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

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

    10.7
  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
  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
  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
  27. How to add Daemon Signing requirements example See Code Signing

    Requirement Language for syntax
  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
  29. How to embed plists in Daemon for SMJobBless() path to

    .plist path to .plist Add 'Other linker flags', shown on the screenshot:
  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.
  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
  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.
  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
  34. Thank you! Happy to help via julia.vashchenko@yahoo.com