1

I have a server on which I am running a daemon as a normal user. I intend to give access to this user in the future to people I trust... but, as a precaution, I would like any configuration and management to be done without su or sudo access. For this reason I've written a simple systemd user unit and tested that it works. It does.

The problem is when restarting the server. The systemd attempts to start the daemon before mariadb (which this daemon needs) is ready. This causes the daemon to fail repeatedly until it exceeds systemd's StartLimit and systemd stops attempting to start the daemon all together.

There are a number of approaches I could take with this. I could re-write the systemd unit to run a script that queries the sql server over and over till it gets a connection and only then attempts to launch the daemon. Or I could add to the systemd unit a timeout between restarts of say... 30 seconds? But both feel... messy... pedestrian. I'd rather a more elegant solution.

I've been looking over the manpages of systemd but... there's a lot there. Any thoughts?

The unit file in question:

[Unit]
Description=Teamspeak 3

[Service]
Type=forking
#User=teamspeak
#Group=teamspeak
UMask=0027
Restart=always
WorkingDirectory=/home/teamspeak/ts
PIDFile=/home/teamspeak/ts/ts3server.pid
ExecStart=/home/teamspeak/ts/ts3server_startscript.sh start inifile=/home/teamspeak/ts/ts3server.ini
ExecStop=/home/teamspeak/ts/ts3server_startscript.sh stop
ExecReload=/home/teamspeak/ts/ts3server_startscript.sh restart

[Install]
WantedBy=default.target
3

One elegant solution is to use socket activation to start your system service, since one of the points of socket activation is to allow starting services which depend on each other without having to specify their dependencies explicitly (which in turn allows their startup to be further parallelized.)

For instance, see this blog post on socket activation which makes this point very clearly:

Socket activation makes it possible to start all four services completely simultaneously, without any kind of ordering. Since the creation of the listening sockets is moved outside of the daemons themselves we can start them all at the same time, and they are able to connect to each other's sockets right-away.

Even though your specific case is that of a cross dependency between a user unit and a system unit, the same logic applies.

Unfortunately, it seems MariaDB currenly doesn't support system activation natively. But you can use a proxy such as systemd-socket-proxyd(8) to create a socket activated service proxying to MariaDB on a separate port. See this question for more details.


Another option is to run your Teamspeak service as a system service rather than a user service, then using the User= directive (as the one you currently have commented) to still run it with your service user.

Since you even describe it as a "daemon", I'd say making it a system service is totally appropriate and perhaps even the more correct approach here.

There are drawbacks, such as making it harder to manage (stop/restart) the service for someone logged in as the "teamspeak" user, but then I'd suggest arrangements that involve switching to a service account have drawbacks (personally I'd avoid this kind of setup) and you could still keep this setup and allow the user to manage the system service using "sudo" or similar.

  • Hmm. Truth be told I've already implemented a script that checks 127.0.0.1:3306 every second for 60 seconds and starts the server process once if can establish any connection (or exits with an error otherwise). But I had hoped for something sleeker... and socket activation looks like just the thing. Pity MariaDB doesn't support it. I'm going to leave the question open for a while longer to see if anyone else has something. – Cliff Armstrong Oct 26 '18 at 11:04
0

Requires=mysqld.service or Wants=mysqld.service directive will help your service dependency.

systemd: Unit dependencies and order on fedora Magazine for details.

  • 1
    Requires= will not work as this is a user unit. Systemd user units run in a completely different process from those of other users and the "system" units. The Requires= directive will simply fail to find a "mysqld.service" unit at all. I've not tried, but I'm quite sure Wants= will have the same problem. – Cliff Armstrong Oct 26 '18 at 2:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.