launchd
is the default service management framework on macOS
since the release of Tiger in 2005. If you want a Fossil server
to launch in the background on a Mac, it’s the way Apple wants you to do
it. launchd
is to macOS as systemd
is to most modern Linux desktop
systems. (Indeed, systemd
arguably reinvented the perfectly good,
pre-existing launchd
wheel.)
Unlike in our systemd
article, we’re not going
to show the per-user method here, because those so-called
LaunchAgents only start when a user is logged into the GUI, and
they stop when that user logs out. This does not strike us as proper
“server” behavior, so we’ll stick to system-level LaunchDaemons instead.
However, we will still give two different configurations, just as in the
systemd
article: one for a standalone HTTP server, and one using
socket activation.
For more information on launchd
, the single best resource we’ve found
is launchd.info. The next best is:
$ man launchd.plist
Standalone HTTP Server
To configure launchd
to start Fossil as a standalone HTTP server,
write the following as com.example.dev.FossilHTTP.plist
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.dev.FossilHTTP</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/fossil</string>
<string>server</string>
<string>--port</string>
<string>9000</string>
<string>repo.fossil</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/you/museum</string>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/tmp/fossil-error.log</string>
<key>StandardOutPath</key>
<string>/tmp/fossil-info.log</string>
<key>UserName</key>
<string>you</string>
<key>GroupName</key>
<string>staff</string>
<key>InitGroups</key>
<true/>
</dict>
</plist>
In this example, we’re assuming your development organization uses the
domain name “dev.example.org
”, that your short macOS login name is
“you
”, and that you store your Fossils in “~/museum
”. Adjust these
elements of the plist file to suit your local situation.
You might be wondering about the use of UserName
: isn’t Fossil
supposed to drop privileges and enter a chroot(2)
jail when it’s started as root like this? Why do we
need to give it a user name? Won’t Fossil use the owner of the
repository file to set that? All I can tell you is that in testing here,
if you leave the user and group configuration at the tail end of that
plist file out, Fossil will remain running as root!
Install that file and set it to start with:
$ sudo install -o root -g wheel -m 644 com.example.dev.FossilHTTP.plist \
/Library/LaunchDaemons/
$ sudo launchctl load -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist
Because we set the RunAtLoad
key, this will also launch the daemon.
Stop the daemon with:
$ sudo launchctl unload -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist
Socket Listener
Another useful method to serve a Fossil repo via launchd
is by setting
up a socket listener:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.dev.FossilSocket</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/fossil</string>
<string>http</string>
<string>repo.fossil</string>
</array>
<key>Sockets</key>
<dict>
<key>Listeners</key>
<dict>
<key>SockServiceName</key>
<string>9001</string>
<key>SockType</key>
<string>stream</string>
<key>SockProtocol</key>
<string>TCP</string>
<key>SockFamily</key>
<string>IPv4</string>
</dict>
</dict>
<key>inetdCompatibility</key>
<dict>
<key>Wait</key>
<false/>
</dict>
<key>WorkingDirectory</key>
<string>/Users/you/museum</string>
<key>UserName</key>
<string>you</string>
<key>GroupName</key>
<string>staff</string>
<key>InitGroups</key>
<true/>
</dict>
</plist>
Save it as “com.example.dev.FossilSocket.plist
” and install and load
it into launchd
as above.
This version differs in several key ways:
We’re calling Fossil as
fossil http
rather thanfossil server
to make it serve a single request and then shut down immediately.We’ve told
launchd
to listen on our TCP port number instead of passing it tofossil
.We’re running the daemon in
inetd
compatibility mode oflaunchd
with “wait” mode off, which tells it to attach the connected socket to thefossil
process’s stdio handles.We’ve removed the
Standard*Path
keys because they interfere with our use of stdio handles for HTTP I/O. You might therefore want to start with the first method and then switch over to this one only once you’ve got the daemon launching debugged, since once you tie up stdio this way, you won’t be able to get logging information from Fossil via that path. (Fossil does have some internal logging mechanisms, but you can’t get at them until Fossil is launching!)We’ve removed the
KeepAlive
andRunAtLoad
keys because those options aren’t appropriate to this type of service.Because we’re running it via a socket listener instead of as a standalone HTTP server, the Fossil service only takes system resources when it’s actually handling an HTTP hit. If your Fossil server is mostly idle, this method will be a bit more efficient than the first option.