systemd
is the default service management framework on
Debian since version 8 and Ubuntu since version 15.04, both
released in April 2015.
There are multiple ways to get a service to launch under systemd
.
We’re going to show two methods which correspond approximately to two of
our generic Fossil server setup methods, the inetd
and standalone HTTP server methods.
User Service
A fun thing you can easily do with systemd
that you can’t directly do
with older technologies like inetd
and xinetd
is to set a server up
as a “user” service.
You can’t listen on TCP port 80 with this method due to security
restrictions on TCP ports in every OS where systemd
runs, but you can
create a listener socket on a high-numbered (≥ 1024) TCP port,
suitable for sharing a Fossil repo to a workgroup on a private LAN.
To do this, write the following in
~/.local/share/systemd/user/fossil.service
:
[Unit]
Description=Fossil user server
After=network.target
[Service]
WorkingDirectory=/home/fossil/museum
ExecStart=/home/fossil/bin/fossil server --port 9000 repo.fossil
Restart=always
RestartSec=3
[Install]
WantedBy=sockets.target
WantedBy=multi-user.target
Unlike with inetd
and xinetd
, we don’t need to tell systemd
which
user and group to run this service as, because we’ve installed it
under the account we’re logged into, which systemd
will use as the
service’s owner.
We’ve told systemd
that we want automatic service restarts with
back-off logic, making this much more robust than the by-hand launches
of fossil
in the platform-independent Fossil server instructions. The
service will stay up until we explicitly tell it to shut down.
A simple and useful modification to the above scheme is to add the
--scgi
and --localhost
flags to the ExecStart
line to replace the
use of fslsrv
in the generic SCGI instructions,
giving a much more robust configuration.
Because we’ve set this up as a user service, the commands you give to manipulate the service vary somewhat from the sort you’re more likely to find online:
$ systemctl --user daemon-reload
$ systemctl --user enable fossil
$ systemctl --user start fossil
$ systemctl --user status -l fossil
$ systemctl --user stop fossil
That is, we don’t need to talk to systemd
with sudo
privileges, but
we do need to tell it to look at the user configuration rather than the
system-level configuration.
This scheme isolates the permissions needed by the Fossil server, which reduces the amount of damage it can do if there is ever a remotely-triggerable security flaw found in Fossil.
On some systemd
based OSes, user services only run while that user is
logged in interactively. This is common on systems aiming to provide
desktop environments, where this is the behavior you often want. To
allow background services to continue to run after logout, say:
$ sudo loginctl enable-linger $USER
You can paste the command just like that into your terminal, since
$USER
will expand to your login name.
System Service Alternative
Another workaround for the problem with user services above is to install the service as a system service instead. This is a better path when you are proxying Fossil with a system-level service, such as nginx.
There are just a small set of changes required:
Install the unit file to one of the persistent system-level unit file directories. Typically, these are:
/etc/systemd/system /lib/systemd/system
Add
User
andGroup
directives to the[Service]
section so Fossil runs as a normal user, preferably one with access only to the Fossil repo files, rather than running asroot
.
Socket Activation
Another useful method to serve a Fossil repo via systemd
is via a
socket listener, which systemd
calls “socket activation.”
It’s more complicated, but it has some nice properties. It is the
feature that allows systemd
to replace inetd
, xinetd
, Upstart, and
several other competing technologies.
We first need to define the privileged socket listener by writing
/etc/systemd/system/fossil.socket
:
[Unit]
Description=Fossil socket
[Socket]
Accept=yes
ListenStream=80
NoDelay=true
[Install]
WantedBy=sockets.target
Note the change of configuration directory from the ~/.local
directory
to the system level. We need to start this socket listener at the root
level because of the low-numbered TCP port restriction we brought up
above.
This configuration says more or less the same thing as the socket part
of an inted
entry exemplified elsewhere in this
documentation.
Next, create the service definition file in that same directory as
fossil@.service
:
[Unit]
Description=Fossil socket server
After=network.target
[Service]
WorkingDirectory=/home/fossil/museum
ExecStart=/home/fossil/bin/fossil http repo.fossil
StandardInput=socket
[Install]
WantedBy=sockets.target
WantedBy=multi-user.target
We’ll explain the “@
” in the file name below.
Notice that we haven’t told systemd
which user and group to run Fossil
under. Since this is a system-level service definition, that means it
will run as root, which then causes Fossil to automatically drop into a
chroot(2)
jail rooted at the WorkingDirectory
we’ve configured above, shortly each fossil http
call starts.
The Restart*
directives we had in the user service configuration above
are unnecessary for this method, since Fossil isn’t supposed to remain
running under it. Each HTTP hit starts one Fossil instance, which
handles that single client’s request and then immediately shuts down.
Next, you need to tell systemd
to reload its system-level
configuration files and enable the listening socket:
$ sudo systemctl daemon-reload
$ sudo systemctl enable fossil.socket
And now you can manipulate the socket listener:
$ sudo systemctl start fossil.socket
$ sudo systemctl status -l fossil.socket
$ sudo systemctl stop fossil.socket
Notice that we’re working with the socket, not the service. The fact
that we’ve given them the same base name and marked the service as an
instantiated service with the “@
” notation allows systemd
to
automatically start an instance of the service each time a hit comes in
on the socket that systemd
is monitoring on Fossil’s behalf. To see
this service instantiation at work, visit a long-running Fossil page
(e.g. /tarball
) and then give a command like this:
$ sudo systemctl --full | grep fossil
This will show information about the fossil
socket and service
instances, which should show your /tarball
hit handler, if it’s still
running:
fossil@20-127.0.0.1:80-127.0.0.1:38304.service
You can feed that service instance description to a systemctl kill
command to stop that single instance without restarting the whole
fossil
service, for example.
In all of this, realize that we’re able to manipulate a single socket
listener or single service instance at a time, rather than reload the
whole externally-facing network configuration as with the far more
primitive inetd
service.