|
f146e21…
|
drh
|
1 |
# Serving via launchd on macOS |
|
f146e21…
|
drh
|
2 |
|
|
f146e21…
|
drh
|
3 |
[`launchd`][ldhome] is the default service management framework on macOS |
|
f146e21…
|
drh
|
4 |
[since the release of Tiger in 2005][wpa]. If you want a Fossil server |
|
f146e21…
|
drh
|
5 |
to launch in the background on a Mac, it’s the way Apple wants you to do |
|
f146e21…
|
drh
|
6 |
it. `launchd` is to macOS as `systemd` is to most modern Linux desktop |
|
f146e21…
|
drh
|
7 |
systems. (Indeed, `systemd` arguably reinvented the perfectly good, |
|
f146e21…
|
drh
|
8 |
pre-existing `launchd` wheel.) |
|
f146e21…
|
drh
|
9 |
|
|
f146e21…
|
drh
|
10 |
Unlike in [our `systemd` article](../debian/service.md), we’re not going |
|
f146e21…
|
drh
|
11 |
to show the per-user method here, because those so-called |
|
f146e21…
|
drh
|
12 |
[LaunchAgents][la] only start when a user is logged into the GUI, and |
|
f146e21…
|
drh
|
13 |
they stop when that user logs out. This does not strike us as proper |
|
f146e21…
|
drh
|
14 |
“server” behavior, so we’ll stick to system-level LaunchDaemons instead. |
|
f146e21…
|
drh
|
15 |
|
|
f146e21…
|
drh
|
16 |
However, we will still give two different configurations, just as in the |
|
f146e21…
|
drh
|
17 |
`systemd` article: one for a standalone HTTP server, and one using |
|
f146e21…
|
drh
|
18 |
socket activation. |
|
f146e21…
|
drh
|
19 |
|
|
f146e21…
|
drh
|
20 |
For more information on `launchd`, the single best resource we’ve found |
|
7de6f15…
|
danield
|
21 |
is [launchd.info](https://launchd.info). The next best is: |
|
f146e21…
|
drh
|
22 |
|
|
8a1ba49…
|
wyoung
|
23 |
$ man launchd.plist |
|
f146e21…
|
drh
|
24 |
|
|
f146e21…
|
drh
|
25 |
[la]: http://www.grivet-tools.com/blog/2014/launchdaemons-vs-launchagents/ |
|
f146e21…
|
drh
|
26 |
[ldhome]: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html |
|
f146e21…
|
drh
|
27 |
[wpa]: https://en.wikipedia.org/wiki/Launchd |
|
f146e21…
|
drh
|
28 |
|
|
f146e21…
|
drh
|
29 |
|
|
f146e21…
|
drh
|
30 |
|
|
f146e21…
|
drh
|
31 |
## Standalone HTTP Server |
|
f146e21…
|
drh
|
32 |
|
|
f146e21…
|
drh
|
33 |
To configure `launchd` to start Fossil as a standalone HTTP server, |
|
f146e21…
|
drh
|
34 |
write the following as `com.example.dev.FossilHTTP.plist`: |
|
f146e21…
|
drh
|
35 |
|
|
f146e21…
|
drh
|
36 |
```xml |
|
8a1ba49…
|
wyoung
|
37 |
<?xml version="1.0" encoding="UTF-8"?> |
|
8a1ba49…
|
wyoung
|
38 |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" |
|
8a1ba49…
|
wyoung
|
39 |
"http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|
8a1ba49…
|
wyoung
|
40 |
<plist version="1.0"> |
|
8a1ba49…
|
wyoung
|
41 |
<dict> |
|
8a1ba49…
|
wyoung
|
42 |
<key>Label</key> |
|
8a1ba49…
|
wyoung
|
43 |
<string>com.example.dev.FossilHTTP</string> |
|
8a1ba49…
|
wyoung
|
44 |
<key>ProgramArguments</key> |
|
8a1ba49…
|
wyoung
|
45 |
<array> |
|
8a1ba49…
|
wyoung
|
46 |
<string>/usr/local/bin/fossil</string> |
|
8a1ba49…
|
wyoung
|
47 |
<string>server</string> |
|
8a1ba49…
|
wyoung
|
48 |
<string>--port</string> |
|
8a1ba49…
|
wyoung
|
49 |
<string>9000</string> |
|
8a1ba49…
|
wyoung
|
50 |
<string>repo.fossil</string> |
|
8a1ba49…
|
wyoung
|
51 |
</array> |
|
8a1ba49…
|
wyoung
|
52 |
<key>WorkingDirectory</key> |
|
8a1ba49…
|
wyoung
|
53 |
<string>/Users/you/museum</string> |
|
8a1ba49…
|
wyoung
|
54 |
<key>KeepAlive</key> |
|
8a1ba49…
|
wyoung
|
55 |
<true/> |
|
8a1ba49…
|
wyoung
|
56 |
<key>RunAtLoad</key> |
|
8a1ba49…
|
wyoung
|
57 |
<true/> |
|
8a1ba49…
|
wyoung
|
58 |
<key>StandardErrorPath</key> |
|
8a1ba49…
|
wyoung
|
59 |
<string>/tmp/fossil-error.log</string> |
|
8a1ba49…
|
wyoung
|
60 |
<key>StandardOutPath</key> |
|
8a1ba49…
|
wyoung
|
61 |
<string>/tmp/fossil-info.log</string> |
|
8a1ba49…
|
wyoung
|
62 |
<key>UserName</key> |
|
8a1ba49…
|
wyoung
|
63 |
<string>you</string> |
|
8a1ba49…
|
wyoung
|
64 |
<key>GroupName</key> |
|
8a1ba49…
|
wyoung
|
65 |
<string>staff</string> |
|
8a1ba49…
|
wyoung
|
66 |
<key>InitGroups</key> |
|
8a1ba49…
|
wyoung
|
67 |
<true/> |
|
8a1ba49…
|
wyoung
|
68 |
</dict> |
|
8a1ba49…
|
wyoung
|
69 |
</plist> |
|
f146e21…
|
drh
|
70 |
``` |
|
f146e21…
|
drh
|
71 |
|
|
f146e21…
|
drh
|
72 |
In this example, we’re assuming your development organization uses the |
|
f146e21…
|
drh
|
73 |
domain name “`dev.example.org`”, that your short macOS login name is |
|
f146e21…
|
drh
|
74 |
“`you`”, and that you store your Fossils in “`~/museum`”. Adjust these |
|
f146e21…
|
drh
|
75 |
elements of the plist file to suit your local situation. |
|
f146e21…
|
drh
|
76 |
|
|
f146e21…
|
drh
|
77 |
You might be wondering about the use of `UserName`: isn’t Fossil |
|
f146e21…
|
drh
|
78 |
supposed to drop privileges and enter [a `chroot(2)` |
|
f146e21…
|
drh
|
79 |
jail](../../chroot.md) when it’s started as root like this? Why do we |
|
f146e21…
|
drh
|
80 |
need to give it a user name? Won’t Fossil use the owner of the |
|
f146e21…
|
drh
|
81 |
repository file to set that? All I can tell you is that in testing here, |
|
f146e21…
|
drh
|
82 |
if you leave the user and group configuration at the tail end of that |
|
f146e21…
|
drh
|
83 |
plist file out, Fossil will remain running as root! |
|
f146e21…
|
drh
|
84 |
|
|
f146e21…
|
drh
|
85 |
Install that file and set it to start with: |
|
f146e21…
|
drh
|
86 |
|
|
8a1ba49…
|
wyoung
|
87 |
$ sudo install -o root -g wheel -m 644 com.example.dev.FossilHTTP.plist \ |
|
8a1ba49…
|
wyoung
|
88 |
/Library/LaunchDaemons/ |
|
8a1ba49…
|
wyoung
|
89 |
$ sudo launchctl load -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist |
|
f146e21…
|
drh
|
90 |
|
|
f146e21…
|
drh
|
91 |
Because we set the `RunAtLoad` key, this will also launch the daemon. |
|
f146e21…
|
drh
|
92 |
|
|
f146e21…
|
drh
|
93 |
Stop the daemon with: |
|
f146e21…
|
drh
|
94 |
|
|
8a1ba49…
|
wyoung
|
95 |
$ sudo launchctl unload -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist |
|
f146e21…
|
drh
|
96 |
|
|
f146e21…
|
drh
|
97 |
|
|
f146e21…
|
drh
|
98 |
## Socket Listener |
|
f146e21…
|
drh
|
99 |
|
|
f146e21…
|
drh
|
100 |
Another useful method to serve a Fossil repo via `launchd` is by setting |
|
f146e21…
|
drh
|
101 |
up a socket listener: |
|
f146e21…
|
drh
|
102 |
|
|
f146e21…
|
drh
|
103 |
```xml |
|
f146e21…
|
drh
|
104 |
<?xml version="1.0" encoding="UTF-8"?> |
|
8a1ba49…
|
wyoung
|
105 |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" |
|
8a1ba49…
|
wyoung
|
106 |
"http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|
f146e21…
|
drh
|
107 |
<plist version="1.0"> |
|
f146e21…
|
drh
|
108 |
<dict> |
|
f146e21…
|
drh
|
109 |
<key>Label</key> |
|
f146e21…
|
drh
|
110 |
<string>com.example.dev.FossilSocket</string> |
|
f146e21…
|
drh
|
111 |
<key>ProgramArguments</key> |
|
f146e21…
|
drh
|
112 |
<array> |
|
f146e21…
|
drh
|
113 |
<string>/usr/local/bin/fossil</string> |
|
f146e21…
|
drh
|
114 |
<string>http</string> |
|
f146e21…
|
drh
|
115 |
<string>repo.fossil</string> |
|
f146e21…
|
drh
|
116 |
</array> |
|
f146e21…
|
drh
|
117 |
<key>Sockets</key> |
|
f146e21…
|
drh
|
118 |
<dict> |
|
f146e21…
|
drh
|
119 |
<key>Listeners</key> |
|
f146e21…
|
drh
|
120 |
<dict> |
|
f146e21…
|
drh
|
121 |
<key>SockServiceName</key> |
|
f146e21…
|
drh
|
122 |
<string>9001</string> |
|
f146e21…
|
drh
|
123 |
<key>SockType</key> |
|
f146e21…
|
drh
|
124 |
<string>stream</string> |
|
f146e21…
|
drh
|
125 |
<key>SockProtocol</key> |
|
f146e21…
|
drh
|
126 |
<string>TCP</string> |
|
f146e21…
|
drh
|
127 |
<key>SockFamily</key> |
|
f146e21…
|
drh
|
128 |
<string>IPv4</string> |
|
f146e21…
|
drh
|
129 |
</dict> |
|
f146e21…
|
drh
|
130 |
</dict> |
|
f146e21…
|
drh
|
131 |
<key>inetdCompatibility</key> |
|
f146e21…
|
drh
|
132 |
<dict> |
|
f146e21…
|
drh
|
133 |
<key>Wait</key> |
|
f146e21…
|
drh
|
134 |
<false/> |
|
f146e21…
|
drh
|
135 |
</dict> |
|
f146e21…
|
drh
|
136 |
<key>WorkingDirectory</key> |
|
f146e21…
|
drh
|
137 |
<string>/Users/you/museum</string> |
|
f146e21…
|
drh
|
138 |
<key>UserName</key> |
|
f146e21…
|
drh
|
139 |
<string>you</string> |
|
f146e21…
|
drh
|
140 |
<key>GroupName</key> |
|
f146e21…
|
drh
|
141 |
<string>staff</string> |
|
f146e21…
|
drh
|
142 |
<key>InitGroups</key> |
|
f146e21…
|
drh
|
143 |
<true/> |
|
f146e21…
|
drh
|
144 |
</dict> |
|
f146e21…
|
drh
|
145 |
</plist> |
|
f146e21…
|
drh
|
146 |
``` |
|
f146e21…
|
drh
|
147 |
|
|
f146e21…
|
drh
|
148 |
Save it as “`com.example.dev.FossilSocket.plist`” and install and load |
|
f146e21…
|
drh
|
149 |
it into `launchd` as above. |
|
f146e21…
|
drh
|
150 |
|
|
f146e21…
|
drh
|
151 |
This version differs in several key ways: |
|
f146e21…
|
drh
|
152 |
|
|
f146e21…
|
drh
|
153 |
1. We’re calling Fossil as `fossil http` rather than `fossil server` to |
|
f146e21…
|
drh
|
154 |
make it serve a single request and then shut down immediately. |
|
f146e21…
|
drh
|
155 |
|
|
f146e21…
|
drh
|
156 |
2. We’ve told `launchd` to listen on our TCP port number instead of |
|
f146e21…
|
drh
|
157 |
passing it to `fossil`. |
|
f146e21…
|
drh
|
158 |
|
|
f146e21…
|
drh
|
159 |
3. We’re running the daemon in `inetd` compatibility mode of `launchd` |
|
f146e21…
|
drh
|
160 |
with “wait” mode off, which tells it to attach the connected socket |
|
f146e21…
|
drh
|
161 |
to the `fossil` process’s stdio handles. |
|
f146e21…
|
drh
|
162 |
|
|
f146e21…
|
drh
|
163 |
4. We’ve removed the `Standard*Path` keys because they interfere with |
|
f146e21…
|
drh
|
164 |
our use of stdio handles for HTTP I/O. You might therefore want to |
|
f146e21…
|
drh
|
165 |
start with the first method and then switch over to this one only |
|
f146e21…
|
drh
|
166 |
once you’ve got the daemon launching debugged, since once you tie up |
|
f146e21…
|
drh
|
167 |
stdio this way, you won’t be able to get logging information from |
|
f146e21…
|
drh
|
168 |
Fossil via that path. (Fossil does have some internal logging |
|
f146e21…
|
drh
|
169 |
mechanisms, but you can’t get at them until Fossil is launching!) |
|
f146e21…
|
drh
|
170 |
|
|
f146e21…
|
drh
|
171 |
5. We’ve removed the `KeepAlive` and `RunAtLoad` keys because those |
|
f146e21…
|
drh
|
172 |
options aren’t appropriate to this type of service. |
|
f146e21…
|
drh
|
173 |
|
|
f146e21…
|
drh
|
174 |
6. Because we’re running it via a socket listener instead of as a |
|
f146e21…
|
drh
|
175 |
standalone HTTP server, the Fossil service only takes system |
|
f146e21…
|
drh
|
176 |
resources when it’s actually handling an HTTP hit. If your Fossil |
|
f146e21…
|
drh
|
177 |
server is mostly idle, this method will be a bit more efficient than |
|
f146e21…
|
drh
|
178 |
the first option. |
|
f146e21…
|
drh
|
179 |
|
|
f146e21…
|
drh
|
180 |
|
|
f146e21…
|
drh
|
181 |
*[Return to the top-level Fossil server article.](../)* |