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