Sign in Join
Wiki copytemporary

macOS daemons and agents: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11*(macOS only)*
22
33## Overview
44
55UNIX-based operating systems have **daemons** or programs that run as background processes in the system context (eg named - the Internet Domain Name Server which is a daemon responsible for resolving domain name queries). Daemons are therefore better described as system background processes and should not have a graphical user interface. They are system-wide background processes of which there is only one instance for all clients.
66
77**Agents** are very similar to daemons in that they are both programs that run as background processes, but in the case of agents they are not running in the system context but in the context of an interactive user's session. Agents are therefore better described as user background processes and may have a graphical user interface or communicate with the user through the macOS system menu bar (eg temperature monitors generally display the status in the system menu bar). They are per-user background processes.
88
99Daemons and agents are each useful in their own right, but each has its own specific use cases. jobs that rely on system-level access should be run as daemons in the system context and jobs that do not require system-level access should be run as agents in the user's context.
1010
1111*Note: in the rest of this article the generic term daemon will be used to refer to both daemons and agents unless required otherwise by the context.*
1212
1313**launchd** is the macOS system-wide and per-user daemon manager which is started by the kernel during the system boot process. It manages processes, both for the system as a whole and for individual users. launchd provides the following benefits to daemon developers:
1414
1515- Simplifies the process of making a daemon by handling many of the standard housekeeping chores normally associated with launching a daemon.
1616- Provides system administrators with a central place to manage daemons on the system.
1717- Supports inetd-style daemons.
1818- Eliminates the primary reason for running daemons as root. Because launchd runs as root, it can create low-numbered TCP/IP listen sockets and hand them off to the daemon.
1919- Simplifies error handling and dependency management for inter-daemon communication. As daemons can be launched on demand, communication requests do not fail if the daemon is not launched. They are simply delayed until the daemon can launch and process them.
2020
2121## The launchd startup process
2222
2323After the system is booted and the kernel is running, launchd is run to finish the system initialization. As part of that initialization, it goes through the following steps:
2424
2525- Loads the parameters for each launch-on-demand system-level daemon from the property list files found in `/System/Library/LaunchDaemons/` and `/Library/LaunchDaemons/`.
2626- Registers the sockets and file descriptors requested by those daemons.
2727- Launches any daemons that requested to be running all the time.
2828- As requests for a particular service arrive, it launches the corresponding daemon and passes the request to it.
2929- When the system shuts down, it sends a SIGTERM signal to all of the daemons that it started.
3030
3131The process for per-user agents is similar. When a user logs in, a per-user launchd is started. It does the following:
3232
3333- Loads the parameters for each launch-on-demand user agent from the property list files found in `/System/Library/LaunchAgents/`, `/Library/LaunchAgents/`, and the user’s individual `~/Library/LaunchAgents/` directory.
3434- Registers the sockets and file descriptors requested by those user agents.
3535- Launches any user agents that requested to be running all the time.
3636- As requests for a particular service arrive, it launches the corresponding user agent and passes the request to it.
3737- When the user logs out, it sends a SIGTERM signal to all of the user agents that it started.
3838
3939As launchd registers the sockets and file descriptors used by all daemons before it launches any of them, daemons can be launched in any order. If a request comes in for a daemon that is not yet running, the requesting process is suspended until the target daemon finishes launching and responds.
4040
4141If a daemon does not receive any requests over a specific period of time, it can choose to shut itself down and release the resources it holds. When this happens, launchd monitors the shutdown and makes a note to launch the daemon again when future requests arrive.
4242
4343## launchctl
4444
4545**launchctl** is the primary interface to launchd which, among other things, allows the administrator or user to load or unload daemons **or** agents. Where possible, it is preferred that background jobs are launched on demand, based on criteria specified in their respective [[macOS property list files|property list files]].
4646
4747Some of the useful commands available include:
4848
4949| Command | Description |
5050|----|----|
5151| `launchctl list` | List loaded jobs |
5252| `launchctl list <LABEL>` | List information about this loaded job from its .plist file. |
5353| `launchctl load /Library/LaunchDaemons/<LABEL>.plist` | Load and start a job (system daemon) that is not disabled. |
5454| `launchctl load -w /Library/LaunchDaemons/<LABEL>.plist` | Load, start and mark a job as not disabled (system daemon). Job restarts on next reboot. |
5555| `launchctl unload /Library/LaunchDaemons/<LABEL>.plist` | Unload and stop a job (system daemon). Job restarts on next boot. |
5656| `launchctl unload -w /Library/LaunchDaemons/<LABEL>.plist` | Unload, stop and disable a job (system daemon). Job does not restart on next reboot. |
5757| `launchctl start <LABEL>` | Start a loaded job. Useful when debugging. |
5858| `launchctl stop <LABEL>` | Stop a loaded job. Job may restart immediately if configured to keep running. |
5959| `launchctl restart <LABEL>` | Restart a loaded job. |
6060
6161**Note:** The commands listed above, while documented as "legacy", are simpler to use than the more complicated replacement commands. For the full details, open an Application \> Utilities \> Terminal and type `man launchctl` at the prompt to access the UNIX manual page.
6262
6363## Types of background processes
6464
6565There are four types of background processes in macOS. To select the appropriate type of background process, consider the following:
6666
6767- Whether it does something for the currently logged in user or for all users.
6868- Whether it will be used by single application or by multiple applications.
6969- Whether it ever needs to display a user interface or launch a GUI application.
7070
71-<thead>
72-
73-</thead>
74-<tbody>
75-
76-(Except in a very limited way using IOSurface)</td>
77-
78-</tbody>
71+| Type | Managed by launchd? | Runs in | Can present UI? |
72+|---|---|---|---|
73+| Login item | No* | Per user context | Yes |
74+| XPC service | Yes | Per user context | No · (Except in a very limited way using IOSurface) |
75+| Launch Daemon | Yes | System-wide context | No |
76+| Launch Agent | Yes | Per user context | Best practice: Not recommended |
7977
8078* Login items are started by the per-user instance of launchd, but it does not take any actions to manage them.
8179
8280## Creating a launchd property list file
8381
8482To run your daemon under launchd, you must provide a configuration property list file for it. This file contains information about your daemon, including the list of sockets or file descriptors it uses to process requests. Specifying this information in a property list file lets launchd register the corresponding file descriptors and launch your daemon only after a request arrives for your daemon’s services. The required and recommended keys for all daemons are:
8583
8684| Key | Description |
8785|----|----|
8886| Label | Contains a unique string that identifies your daemon to launchd. (required) |
8987| ProgramArguments | Contains the arguments used to launch your daemon. The first argument is the absolute path to your program executable. (required) |
9088| inetdCompatibility | Indicates that your daemon requires a separate instance per incoming connection. This causes launchd to behave like inetd, passing each daemon a single socket that is already connected to the incoming client. (required if your daemon was designed to be launched by inetd; otherwise, must not be included) |
9189| RunAtLoad \<boolean\> | This optional key is used to control whether your job is launched once at the time the job is loaded. The default is false. |
9290| StartInterval \<integer\> | Optional key causes the job to be started every N seconds. If the computer is asleep, the job will be started the next time it wakes up. If multiple intervals occur before the computer wakes, those events will be coalesced into one event. |
9391| KeepAlive | This key specifies whether your daemon launches on-demand or must always be running. It is recommended that you design your daemon to be launched on-demand and for this reason this key defaults to false. |
9492
9593The full list of keys for daemon property lists may be found in the online UNIX manual page for `launchd.plist` which you can access by opening an Application \> Utilities \> Terminal window and typing `man launchd.plist` at the prompt.
9694
9795The [[macOS property list files|property list file]] is structured the same for both daemons and agents. You indicate whether it describes a daemon or agent by the directory you place it in. Property list files describing daemons are installed in `/Library/LaunchDaemons/`, and those describing agents are installed in `/Library/LaunchAgents/` or in the individual user's `~/Library/LaunchAgents/` subdirectory. The following table lists these directories and describes their correct usage:
9896
9997| Directory | Description |
10098|----|----|
10199| `~/Library/LaunchAgents` | Per-user agents provided by the user. |
102100| `/Library/LaunchAgents` | Per-user agents provided by the administrator. |
103101| `/Library/LaunchDaemons` | System-wide daemons provided by the administrator. |
104102| `/System/Library/LaunchAgents` | Per-user agents provided by macOS. |
105103| `/System/Library/LaunchDaemons` | System-wide daemons provided by macOS. |
106104
107105The appropriate location for the executable program that you launch as your background process is `/usr/local/libexec/`.
108106
109107## Writing daemon code
110108
111109Processes that are managed by launchd must follow certain requirements so that they interact properly with it. This includes launch daemons and launch agents.
112110
113111### Mandatory
114112
115113- You must provide a property list file with some basic launch-on-demand criteria for your daemon. See Creating a launchd Property List File.
116114
117115- You **must not** daemonize your process. No calling any daemon function, fork followed by exec, or fork followed by exit. If you do, launchd will consider your process has died. Depending on your property list key settings, launchd will either keep trying to relaunch your process until it gives up with a "respawning too fast" error or will be unable to restart it if it really does die.
118116
119117- Daemons and agents that are installed globally must be owned by the root user. Agents installed for the current user must be owned by that user. All daemons and agents must not be group or world writable.
120118
121119### Recommended
122120
123121- Wait until your daemon is fully initialized before attempting to process requests. Your daemon should always provide a reasonable response (rather than an error) when processing requests.
124122
125123- Register the sockets and file descriptors used by your daemon in your launchd configuration property list file. If your daemon advertises a socket, check-in with launchd as part of your daemon initialization using the launch_activate_socket() function call.
126124
127125- Provide a handler to catch the SIGTERM signal sent by launchd to shut down your daemon.
128126
129127### Not recommended
130128
131129- Do not set the user or group ID for your daemon. Include the UserName, UID, GroupName, or GID keys in your daemon’s configuration property list file instead.
132130
133131- Do not set the working directory. Include the WorkingDirectory key in your daemon’s configuration property list file instead.
134132
135133- Do not call chroot to change the root directory. Include the RootDirectory key in your daemon’s configuration property list file instead.
136134
137135- Do not call setsid to create a new session.
138136
139137- Do not close any stray file descriptors.
140138
141139- Do not change stdio to point to /dev/null. Include the StandardOutPath or StandardErrorPath keys in your daemon’s configuration property list file instead.
142140
143141- Do not set up resource limits with setrusage. Use the SoftResourceLimits and HardResourceLimit keys in your daemon’s configuration property list file instead.
144142
145143- Do not set the daemon priority with setpriority. Use the nice, ProcessType, LowPriorityIO, LowPriorityBackgroundIO, nice keys in your daemon’s configuration property list file instead.
146144
147145## Examples
148146
149147### Launch Daemon to run a script as a user on a schedule
150148
151149Here is an example of a [[macOS property list files|property list file]] for a system-wide daemon which runs a script as a specific user at 11 o'clock every morning. It will run even when there is no user logged in to the computer provided that the computer is operating.
152150
153151``` xml
154152<?xml version="1.0" encoding="UTF-8"?>
155153<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
156154<plist version="1.0">
157155<dict>
158156 <key>Label</key>
159157 <string>my_daemon.job</string>
160158 <key>UserName</key>
161159 <string>username</string>
162160 <key>WorkingDirectory</key>
163161 <string>/Users/username/work_dir</string>
164162 <key>EnvironmentVariables</key>
165163 <dict>
166164 <key>PATH</key>
167165 <string>/Users/username/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin</string>
168166 <key>SHELL</key>
169167 <string>/bin/sh</string>
170168 </dict>
171169 <key>LowPriorityBackgroundIO</key>
172170 <true/>
173171 <key>Nice</key>
174172 <integer>10</integer>
175173 <key>Program</key>
176174 <string>/Users/username/bin/my_daemon.sh</string>
177175 <key>StandardErrorPath</key>
178176 <string>/Users/username/tmp/my_daemon.stderr</string>
179177 <key>StandardOutPath</key>
180178 <string>/Users/username/tmp/my_daemon.stdout</string>
181179 <key>StartCalendarInterval</key>
182180 <dict>
183181 <key>Hour</key>
184182 <integer>11</integer>
185183 <key>Minute</key>
186184 <integer>0</integer>
187185 </dict>
188186</dict>
189187</plist>
190188```
191189
192190An explanation of the above file:
193191
194192**Line 1:** The XML prolog
195193
196194**Line 2:** The XML DTD for property list files
197195
198196**Line 6:** A unique name identifying this job
199197
200198**Line 8:** The username under which to run the job
201199
202200**Line 10:** Directory to change to before running the job
203201
204202**Line 14:** The path environment variable to use to find commands used in the script
205203
206204**Line 16:** The shell to use
207205
208206**Line 19:** Tells the kernel to consider this daemon to be low priority when doing filesystem I/O when throttled with a background classification
209207
210208**Line 21:** Sets the scheduling priority of this job to 10 (-20 = highest; 20 = lowest)
211209
212210**Line 23:** The script to run
213211
214212**Line 25:** The path to the file to which to map the the job's *stdout* (Useful for debugging; the file is created if it does not exist. If omitted, output is directed to */dev/null*.)
215213
216214**Line 27:** The path to the file to which to map the the job's *stderr* (Useful for debugging; the file is created if it does not exist. If omitted, output is directed to */dev/null*.)
217215
218216**Line 31:** The hour at which to start the job every day
219217
220218**Line 33:** The minute at which to start the job every day (if omitted, the job would run *every* minute!)
221219
222220Here is the example `my_daemon.sh` script (referenced in the property list file above) to be executed:
223221
224222``` bash
225223#!/bin/sh
226224date
227225svn up
228226date
229227```
230228
231229which just prints the date and time, updates the source code in the Subversion repository in `work_dir` and prints the date and time again. The standard output is captured in the `my_daemon.stdout` file and any error output is captured in the `my_daemon.stderr` file. On subsequent runs, these files will be *appended to* and not overwritten.
232230
233231The property list file should be copied to the `/Library/LaunchDaemons/` directory and the file should be owned by root and not group or world writable.
234232
235233To start the job:
236234
237235```text
238236$ sudo launchctl load /Library/LaunchDaemons/my_daemon.plist
239237```
240238
241239To stop the job from executing any more:
242240
243241```text
244242$ sudo launchctl unload /Library/LaunchDaemons/my_daemon.plist
245243```
246244
247245## Launch daemon to run a TCP server
248246
249247Here is an example of a property list file for a system-wide daemon which runs a TCP server. It will run even when there is no user logged in to the computer provided that the computer is operating.
250248
251249``` xml
252250<?xml version="1.0" encoding="UTF-8"?>
253251<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
254252<plist version="1.0">
255253<dict>
256254 <key>Label</key>
257255 <string>my_daemon</string>
258256 <key>ProgramArguments</key>
259257 <array>
260258 <string>/usr/local/bin/my_daemon.sh</string>
261259 </array>
262260 <key>RunAtLoad</key>
263261 <true/>
264262 <key>StandardErrorPath</key>
265263 <string>/Users/username/tmp/my_daemon.stderr</string>
266264 <key>StandardOutPath</key>
267265 <string>/Users/username/tmp/my_daemon.stdout</string>
268266</dict>
269267</plist>
270268```
271269
272270The property list file should be copied to the `/Library/LaunchDaemons/` directory and the file should be owned by root and not group or world writable.
273271
274272This is a simple TCP server daemon which simply prints "Hello Pascal World!" when a client connects to the server's port 12345; after printing that message the server then disconnects the client. For example, with telnet on FreeBSD:
275273
276274```text
277275 $ telnet macmini8 12345
278276 Trying 192.168.1.21...
279277 Connected to macmini8.sentry.org.
280278 Escape character is '^]'.
281279 Hello Pascal World!
282280 Connection closed by foreign host.
283281```
284282
285283In the absence of telnet on macOS, you can use nc (netcat):
286284
287285```text
288286 $ nc localhost 12345
289287 Hello Pascal World!
290288 $
291289```
292290
293291``` pascal
294292//
295293// Heavily based on forum member leledumbo's TCP server at http://pascalgeek.blogspot.com/2012/06/
296294//
297295
298296Program my_daemon;
299297
300298{$mode objfpc}{H+}
301299
302300Uses
303301 cThreads, // needed to handle each client in its own thread
304302 Classes,
305303 SysUtils,
306304 Sockets,
307305 fpSock, // see note
308306 fpAsync,
309307 BaseUnix;
310308
311309Type
312310
313311 { Client handler thread }
314312
315313 TClientHandlerThread = class(TThread)
316314 private
317315 FClientStream: TSocketStream;
318316 public
319317 constructor Create(AClientStream: TSocketStream);
320318 procedure Execute; override;
321319 end;
322320
323321 { TCP server - override constructor to hook when a client connects }
324322
325323 TTestServer = class(TTCPServer)
326324 private
327325 procedure TestOnConnect(Sender: TConnectionBasedSocket; AStream: TSocketStream);
328326 public
329327 constructor Create(AOwner: TComponent); override;
330328 end;
331329
332330{ Helper to obtain and save IP and port as a string }
333331
334332function AddrToString(Addr: TSockAddr): String;
335333begin
336334 Result := NetAddrToStr(Addr.sin_addr) + ':' + IntToStr(Addr.sin_port);
337335end;
338336
339337{ TClientHandlerThread }
340338
341339constructor TClientHandlerThread.Create(AClientStream: TSocketStream);
342340begin
343341 inherited Create(false);
344342 FreeOnTerminate := true;
345343 FClientStream := AClientStream;
346344end;
347345
348346procedure TClientHandlerThread.Execute;
349347begin
350348 try
351349 FClientStream.WriteAnsiString('Hello Pascal World!' + LineEnding);
352350 WriteLn(AddrToString(FClientStream.PeerAddress) + ' connected');
353351 except
354352 on e: EStreamError do begin
355353 WriteLn('EStreamError');;
356354 end;
357355 end;
358356
359357 WriteLn(AddrToString(FClientStream.PeerAddress) + ' disconnected');
360358 FClientStream.Free;
361359end;
362360
363361{ TTestServer }
364362
365363procedure TTestServer.TestOnConnect(Sender: TConnectionBasedSocket; AStream: TSocketStream);
366364begin
367365 WriteLn('Incoming connection from ' + AddrToString(AStream.PeerAddress));
368366 TClientHandlerThread.Create(AStream);
369367end;
370368
371369constructor TTestServer.Create(AOwner: TComponent);
372370begin
373371 inherited;
374372 OnConnect := @TestOnConnect;
375373end;
376374
377375{ main }
378376
379377var
380378 ServerEventLoop: TEventLoop;
381379
382380begin
383381 ServerEventLoop := TEventLoop.Create;
384382 with TTestServer.Create(nil) do
385383 begin
386384 EventLoop := ServerEventLoop;
387385 Port := 12345;
388386 WriteLn('Serving...');
389387 Active := true;
390388 EventLoop.Run;
391389 end;
392390end.
393391```
394392
395393**Note:** The unit `fpSock` is currently not being compiled for macOS. You will need to copy it from `/usr/local/share/fpcsrc/3.x.x/packages/fcl-net/src/fpsock.pp` to your project directory and then substitute SO_NOSIGPIPE for MSG_NOSIGNAL in the two places where it occurs in that file. Now when you compile your project it should be found, compiled and linked.
396394
397395The `my_daemon` executable file should be copied to `/usr/local/bin/` and should be owned by root and not group or world writable.
398396
399397The script `/usr/local/bin/my_daemon.sh` (referenced in the property list file above) for Launchd to start the Pascal TCP server daemon executable is:
400398
401399``` bash
402400#!/bin/sh
403401/usr/local/bin/my_daemon
404402```
405403
406404To start the TCP server, execute:
407405
408406```text
409407$ sudo launchctl load /Library/LaunchDaemons/my_daemon.plist
410408```
411409
412410To stop the TCP server, execute:
413411
414412```text
415413$ sudo launchctl unload /Library/LaunchDaemons/my_daemon.plist
416414```
417415
418416**A confession:** This is not the recommended method of launching a TCP server. In theory, the socket name (port) should be listed in the property list file and the macOS launch_activate_socket() function call should take care of setting up the socket. Unfortunately, while I have managed to get launch_activate_socket() working in C, I have so far failed to translate it successfully into Pascal. In the meantime, this somewhat inelegant workaround (using a script to launch the TCP server) works.
419417
420418## See also
421419
422420- [[Daemons and Services|Cross-platform daemons and services]].
423421
424422## External links
425423
426424- [Apple: Technical Note: Daemons and Agents](https://developer.apple.com/library/archive/technotes/tn2083/_index.html).
427425- [Apple: Daemons and Services Programming Guide](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html).
428426
429427---
430428*From the Free Pascal wiki, page "macOS daemons and agents", as it stood on 2021-05-04 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
431429Category: Code
432430Category: Tutorials
433431Category: macOS

Back to history