2

Regarding systemd, is it possible to pause or stop a running service (ensuring all data have been written) while another service is running?

My scenario looks like this: I have built a sensor (based upon a Raspberry Pi Zero W and running Kali Linux). The daemon process for sensing is starting (as systemd service unit sensor.service) right after the boot process finishes.

[Unit]
Description=Arbitrary sensor
After=network.target

[Service]
ExecStart=/usr/local/bin/sense --daemon

[Install]
WantedBy=multi-user.target

The sensor is collecting and storing the data locally and continuously. The data is being computed periodically and its result shall be sent away (over the network). Now, for the time of computation the sensor shall stop receiving further data to ensure all data have been written, before the computation starts. Right after the computation is done, the sensor shall start to collect further data.

Yes, I could write a script by myself which handles that:

systemctl stop sensor.service
systemctl start compute.service  # Terminates itself
systemctl start sensor.service

But I wonder, whether this is a scenario that can be handled by systemd for me? Especially without the need to write a script by myself. What I'm imaging is a compute.timer unit which handles the three lines above.

3
+50

The script you wrote in your question has a small problem. It doesn't wait for compute.service to stop before starting sensor.service.


Strategies

Here are four strategies. I think the third one is my favorite.

Find another means of concurrency

I'm not sure that stopping sensor.service is necessary. It sounds like the reason you would do this is so that new data doesn't affect the computation. In that case, I'd take a snapshot of the data file. Then compute off of that snapshot. That snapshot won't update during the compute process, solving your concurrency problem. Using PrivateTmp= means that your /tmp is actually a fresh temporary mount which is deleted when the service stops.

# /etc/systemd/system/compute.service
[Service]
ExecStartPre=/bin/cp /var/lib/sensor/data /tmp/data_to_compute
ExecStart=/usr/bin/compute /tmp/data_to_compute
PrivateTmp=yes

Do it all in a timed script

If it really is necessary to stop sensor.service, your script idea isn't bad. I'd probably make compute.timer which calls compute.service which contains ExecStart=/usr/local/bin/compute.sh. That would contain something like this:

#!/bin/sh
systemctl stop sensor.service
/usr/bin/compute /var/lib/sensor/data
systemctl start sensor.service

Use systemd relationships

In the previous example, we called systemctl from inside a script run by systemd. Something about that just doesn't feel right. The Conflicts= relationship in systemd means that when the service is started, it will automatically stop the conflicting service. systemd doesn't have a relationship to start a process when another finishes, so we can use ExecStartPost= for that here:

Therefore adding this to compute.service:

[Unit]
Conflicts=sensor.service

[Service]
ExecStart=/usr/bin/compute /var/lib/sensor/data
ExecStopPost=systemctl start sensor.service

will achieve your goal of stopping sensor.service whenever compute.timer triggers. Instead of a custom shell script, we have moved everything into the unit file. The discomfort of using systemctl from inside of systemd isn't solved, but at least we've reduced it and made it more transparent (instead of hiding it in a script).

Two timers

This is a little in-elegant, but if you want to replace the ExecStopPost= line, you could have two timers. compute.timer is set to compute as frequently as you like (just like you are already doing). If you want to compute 5 minutes worth of data, then set sensor.timer to launch sensor.service 5 minutes earlier than you trigger compute.timer.


Putting it all togeather

Regardless of the strategy, you'll need compute.timer and compute.service, but shouldn't need to touch sensor.service. I realized that you might have been asking how to make a systemd timer. Here is how I would completely implement strategy 3:

# /etc/systemd/system/compute.timer
[Unit]
Description=Timer to trigger computations every 15 minutes

[Timer]
OnCalendar=*:0/15

[Install]
WantedBy=timers.target

compute.timer (above) will start compute.service (below) every 15 minutes.

# /etc/systemd/system/compute.service
[Unit]
Description=Runs computation (triggered by compute.timer)
Conflicts=sensor.service

[Service]
ExecStart=/usr/bin/compute /var/lib/sensor/data
ExecStopPost=systemctl start sensor.service

Then just enable the timer (and start if you don't want to reboot):

systemctl enable compute.timer
systemctl start compute.timer
1
  • This is really a very good compendium of possible solutions to my problem. Also my favorite is the 3rd one. Thank you +1 – mrkskwsnck Aug 18 '20 at 7:18
0

The best practices to face my problem I found so far, are described in another question: https://unix.stackexchange.com/a/318744/208465

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.