Sign in Join
Wiki copytemporary

macOS daemons and agents: what changed

Revision 1 to 2. 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
7171<thead>
7272
7373</thead>
7474<tbody>
7575
7676(Except in a very limited way using IOSurface)</td>
7777
7878</tbody>
7979
8080* Login items are started by the per-user instance of launchd, but it does not take any actions to manage them.
8181
8282## Creating a launchd property list file
8383
8484To 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:
8585
8686| Key | Description |
8787|----|----|
8888| Label | Contains a unique string that identifies your daemon to launchd. (required) |
8989| ProgramArguments | Contains the arguments used to launch your daemon. The first argument is the absolute path to your program executable. (required) |
9090| 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) |
9191| 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. |
9292| 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. |
9393| 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. |
9494
9595The 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.
9696
9797The [[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:
9898
9999| Directory | Description |
100100|----|----|
101101| `~/Library/LaunchAgents` | Per-user agents provided by the user. |
102102| `/Library/LaunchAgents` | Per-user agents provided by the administrator. |
103103| `/Library/LaunchDaemons` | System-wide daemons provided by the administrator. |
104104| `/System/Library/LaunchAgents` | Per-user agents provided by macOS. |
105105| `/System/Library/LaunchDaemons` | System-wide daemons provided by macOS. |
106106
107107The appropriate location for the executable program that you launch as your background process is `/usr/local/libexec/`.
108108
109109## Writing daemon code
110110
111111Processes that are managed by launchd must follow certain requirements so that they interact properly with it. This includes launch daemons and launch agents.
112112
113113### Mandatory
114114
115115- You must provide a property list file with some basic launch-on-demand criteria for your daemon. See Creating a launchd Property List File.
116116
117117- 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.
118118
119119- 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.
120120
121121### Recommended
122122
123123- 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.
124124
125125- 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.
126126
127127- Provide a handler to catch the SIGTERM signal sent by launchd to shut down your daemon.
128128
129129### Not recommended
130130
131131- 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.
132132
133133- Do not set the working directory. Include the WorkingDirectory key in your daemon’s configuration property list file instead.
134134
135135- Do not call chroot to change the root directory. Include the RootDirectory key in your daemon’s configuration property list file instead.
136136
137137- Do not call setsid to create a new session.
138138
139139- Do not close any stray file descriptors.
140140
141141- Do not change stdio to point to /dev/null. Include the StandardOutPath or StandardErrorPath keys in your daemon’s configuration property list file instead.
142142
143143- Do not set up resource limits with setrusage. Use the SoftResourceLimits and HardResourceLimit keys in your daemon’s configuration property list file instead.
144144
145145- 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.
146146
147147## Examples
148148
149149### Launch Daemon to run a script as a user on a schedule
150150
151151Here 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.
152152
153153``` xml
154154<?xml version="1.0" encoding="UTF-8"?>
155155<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
156156<plist version="1.0">
157157<dict>
158158 <key>Label</key>
159159 <string>my_daemon.job</string>
160160 <key>UserName</key>
161161 <string>username</string>
162162 <key>WorkingDirectory</key>
163163 <string>/Users/username/work_dir</string>
164164 <key>EnvironmentVariables</key>
165165 <dict>
166166 <key>PATH</key>
167167 <string>/Users/username/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin</string>
168168 <key>SHELL</key>
169169 <string>/bin/sh</string>
170170 </dict>
171171 <key>LowPriorityBackgroundIO</key>
172172 <true/>
173173 <key>Nice</key>
174174 <integer>10</integer>
175175 <key>Program</key>
176176 <string>/Users/username/bin/my_daemon.sh</string>
177177 <key>StandardErrorPath</key>
178178 <string>/Users/username/tmp/my_daemon.stderr</string>
179179 <key>StandardOutPath</key>
180180 <string>/Users/username/tmp/my_daemon.stdout</string>
181181 <key>StartCalendarInterval</key>
182182 <dict>
183183 <key>Hour</key>
184184 <integer>11</integer>
185185 <key>Minute</key>
186186 <integer>0</integer>
187187 </dict>
188188</dict>
189189</plist>
190190```
191191
192192An explanation of the above file:
193193
194194**Line 1:** The XML prolog
195195
196196**Line 2:** The XML DTD for property list files
197197
198198**Line 6:** A unique name identifying this job
199199
200200**Line 8:** The username under which to run the job
201201
202202**Line 10:** Directory to change to before running the job
203203
204204**Line 14:** The path environment variable to use to find commands used in the script
205205
206206**Line 16:** The shell to use
207207
208208**Line 19:** Tells the kernel to consider this daemon to be low priority when doing filesystem I/O when throttled with a background classification
209209
210210**Line 21:** Sets the scheduling priority of this job to 10 (-20 = highest; 20 = lowest)
211211
212212**Line 23:** The script to run
213213
214214**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*.)
215215
216216**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*.)
217217
218218**Line 31:** The hour at which to start the job every day
219219
220220**Line 33:** The minute at which to start the job every day (if omitted, the job would run *every* minute!)
221221
222222Here is the example `my_daemon.sh` script (referenced in the property list file above) to be executed:
223223
224224``` bash
225225#!/bin/sh
226226date
227227svn up
228228date
229229```
230230
231231which 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.
232232
233233The 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.
234234
235235To start the job:
236236
237-`$ sudo launchctl load /Library/LaunchDaemons/my_daemon.plist`
237+```text
238+$ sudo launchctl load /Library/LaunchDaemons/my_daemon.plist
239+```
238240
239241To stop the job from executing any more:
240242
241-`$ sudo launchctl unload /Library/LaunchDaemons/my_daemon.plist`
243+```text
244+$ sudo launchctl unload /Library/LaunchDaemons/my_daemon.plist
245+```
242246
243247## Launch daemon to run a TCP server
244248
245249Here 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.
246250
247251``` xml
248252<?xml version="1.0" encoding="UTF-8"?>
249253<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
250254<plist version="1.0">
251255<dict>
252256 <key>Label</key>
253257 <string>my_daemon</string>
254258 <key>ProgramArguments</key>
255259 <array>
256260 <string>/usr/local/bin/my_daemon.sh</string>
257261 </array>
258262 <key>RunAtLoad</key>
259263 <true/>
260264 <key>StandardErrorPath</key>
261265 <string>/Users/username/tmp/my_daemon.stderr</string>
262266 <key>StandardOutPath</key>
263267 <string>/Users/username/tmp/my_daemon.stdout</string>
264268</dict>
265269</plist>
266270```
267271
268272The 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.
269273
270274This 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:
271275
272-` $ telnet macmini8 12345`\
273-` Trying 192.168.1.21...`\
274-` Connected to macmini8.sentry.org.`\
275-` Escape character is '^]'.`\
276-` Hello Pascal World!`\
277-` Connection closed by foreign host.`
276+```text
277+ $ telnet macmini8 12345
278+ Trying 192.168.1.21...
279+ Connected to macmini8.sentry.org.
280+ Escape character is '^]'.
281+ Hello Pascal World!
282+ Connection closed by foreign host.
283+```
278284
279285In the absence of telnet on macOS, you can use nc (netcat):
280286
281-` $ nc localhost 12345`\
282-` Hello Pascal World!`\
283-` $`
287+```text
288+ $ nc localhost 12345
289+ Hello Pascal World!
290+ $
291+```
284292
285293``` pascal
286294//
287295// Heavily based on forum member leledumbo's TCP server at http://pascalgeek.blogspot.com/2012/06/
288296//
289297
290298Program my_daemon;
291299
292300{$mode objfpc}{H+}
293301
294302Uses
295303 cThreads, // needed to handle each client in its own thread
296304 Classes,
297305 SysUtils,
298306 Sockets,
299307 fpSock, // see note
300308 fpAsync,
301309 BaseUnix;
302310
303311Type
304312
305313 { Client handler thread }
306314
307315 TClientHandlerThread = class(TThread)
308316 private
309317 FClientStream: TSocketStream;
310318 public
311319 constructor Create(AClientStream: TSocketStream);
312320 procedure Execute; override;
313321 end;
314322
315323 { TCP server - override constructor to hook when a client connects }
316324
317325 TTestServer = class(TTCPServer)
318326 private
319327 procedure TestOnConnect(Sender: TConnectionBasedSocket; AStream: TSocketStream);
320328 public
321329 constructor Create(AOwner: TComponent); override;
322330 end;
323331
324332{ Helper to obtain and save IP and port as a string }
325333
326334function AddrToString(Addr: TSockAddr): String;
327335begin
328336 Result := NetAddrToStr(Addr.sin_addr) + ':' + IntToStr(Addr.sin_port);
329337end;
330338
331339{ TClientHandlerThread }
332340
333341constructor TClientHandlerThread.Create(AClientStream: TSocketStream);
334342begin
335343 inherited Create(false);
336344 FreeOnTerminate := true;
337345 FClientStream := AClientStream;
338346end;
339347
340348procedure TClientHandlerThread.Execute;
341349begin
342350 try
343351 FClientStream.WriteAnsiString('Hello Pascal World!' + LineEnding);
344352 WriteLn(AddrToString(FClientStream.PeerAddress) + ' connected');
345353 except
346354 on e: EStreamError do begin
347355 WriteLn('EStreamError');;
348356 end;
349357 end;
350358
351359 WriteLn(AddrToString(FClientStream.PeerAddress) + ' disconnected');
352360 FClientStream.Free;
353361end;
354362
355363{ TTestServer }
356364
357365procedure TTestServer.TestOnConnect(Sender: TConnectionBasedSocket; AStream: TSocketStream);
358366begin
359367 WriteLn('Incoming connection from ' + AddrToString(AStream.PeerAddress));
360368 TClientHandlerThread.Create(AStream);
361369end;
362370
363371constructor TTestServer.Create(AOwner: TComponent);
364372begin
365373 inherited;
366374 OnConnect := @TestOnConnect;
367375end;
368376
369377{ main }
370378
371379var
372380 ServerEventLoop: TEventLoop;
373381
374382begin
375383 ServerEventLoop := TEventLoop.Create;
376384 with TTestServer.Create(nil) do
377385 begin
378386 EventLoop := ServerEventLoop;
379387 Port := 12345;
380388 WriteLn('Serving...');
381389 Active := true;
382390 EventLoop.Run;
383391 end;
384392end.
385393```
386394
387395**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.
388396
389397The `my_daemon` executable file should be copied to `/usr/local/bin/` and should be owned by root and not group or world writable.
390398
391399The 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:
392400
393401``` bash
394402#!/bin/sh
395403/usr/local/bin/my_daemon
396404```
397405
398406To start the TCP server, execute:
399407
400-`$ sudo launchctl load /Library/LaunchDaemons/my_daemon.plist`
408+```text
409+$ sudo launchctl load /Library/LaunchDaemons/my_daemon.plist
410+```
401411
402412To stop the TCP server, execute:
403413
404-`$ sudo launchctl unload /Library/LaunchDaemons/my_daemon.plist`
414+```text
415+$ sudo launchctl unload /Library/LaunchDaemons/my_daemon.plist
416+```
405417
406418**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.
407419
408420## See also
409421
410422- [[Daemons and Services|Cross-platform daemons and services]].
411423
412424## External links
413425
414426- [Apple: Technical Note: Daemons and Agents](https://developer.apple.com/library/archive/technotes/tn2083/_index.html).
415427- [Apple: Daemons and Services Programming Guide](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html).
416428
417429---
418430*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).*
419431Category: Code
420432Category: Tutorials
421433Category: macOS

Back to history