You are not logged in.

#1 2021-10-09 20:14:37

Jason P.
Member
Registered: 2015-02-26
Posts: 171

[solved] Advice writing a systemd service unit

Hi,

I want to write a unit that basically does this: ping a hostname and if that works call another executable. In case networking is not available the unit should cancel itself after a given amount of time. It's important to note that this unit will run on every boot so it doesn't have to block the rest of the boot process in any case.

For these requirements I think that a unit Type=simple in combination with RuntimeMaxSec= will do the trick. Very straight forward.

These are the service lines:

[Service]
ExecStart=/bin/sh -c 'until ping -c1 example.com; do sleep 1; done;'
ExecStartPost=/usr/bin/foo bar
RuntimeMaxSec=15

Any suggestions or comments are welcome smile

Last edited by Jason P. (2021-10-13 09:21:20)

Offline

#2 2021-10-09 23:34:04

Wild Penguin
Member
Registered: 2015-03-19
Posts: 319

Re: [solved] Advice writing a systemd service unit

Does your example actually work? Do you need systemd to keep track of the service (i.e. it should be reported as failed if it can not ping the host)?

If both answers are yes, then I'd say just keep using it smile.

For more complex shell commands I would perhaps use a script run by the systemd service. You could use return codes to signal systemd if the service has failed, and write simpler systemd units. But this is coming from a hobbyist - I'm not sure what are the possible downsides or upsides to either approach.

Last edited by Wild Penguin (2021-10-09 23:34:49)

Offline

#3 2021-10-11 12:02:54

Jason P.
Member
Registered: 2015-02-26
Posts: 171

Re: [solved] Advice writing a systemd service unit

Well, I was asking for advice before actually trying it smile I will write it this way and see.

Thanks.

Offline

#4 2021-10-11 13:59:34

schard
Member
From: Hannover
Registered: 2016-05-06
Posts: 1,932
Website

Re: [solved] Advice writing a systemd service unit

Jason P. wrote:

I want to write a unit that basically does this: ping a hostname and if that works call another executable. In case networking is not available the unit should cancel itself after a given amount of time. It's important to note that this unit will run on every boot so it doesn't have to block the rest of the boot process in any case.

This sounds like an implied dependecy chain.
I'd write two units for that:

ping-example-dot-com.service

[Unit]
Description=Ping example.com
Requires=network-online.target
After=network-online.target

[Service]
ExecStart=/bin/ping -c 1 -w 15 example.com
RemainAfterExit=true

foo.service

[Unit]
Description=Foo the bar
Requires=ping-example-dot-com.service
After=ping-example-dot-com.service

[Service]
Type=oneshot
ExecStart=/usr/bin/foo bar

Offline

#5 2021-10-11 15:38:54

Jason P.
Member
Registered: 2015-02-26
Posts: 171

Re: [solved] Advice writing a systemd service unit

schard wrote:

This sounds like an implied dependecy chain.
I'd write two units for that:

ping-example-dot-com.service

[Unit]
Description=Ping example.com
Requires=network-online.target
After=network-online.target

[Service]
ExecStart=/bin/ping -c 1 -w 15 example.com
RemainAfterExit=true

foo.service

[Unit]
Description=Foo the bar
Requires=ping-example-dot-com.service
After=ping-example-dot-com.service

[Service]
Type=oneshot
ExecStart=/usr/bin/foo bar

Great piece of advice smile

I finally went with your way.

Offline

#6 2021-10-11 17:02:09

Jason P.
Member
Registered: 2015-02-26
Posts: 171

Re: [solved] Advice writing a systemd service unit

@schard I've just ran into a case where foo.service ran with a failed ping. No ordering was respected. I thik the issue lies with the type of unit of foo.service, which is Type=simple. In this case systemd assumes everything goes fine and starts following units after forking the process and it doesn't care about the final result.

In my case I have a strict requirement of foo.service happening only when ping succeeds. If ping fails, foo.service must fail.

If set to simple (the default if ExecStart= is specified but neither Type= nor BusName= are), the service manager will consider the unit started immediately after the main service process has been forked off. It is expected that the process configured with ExecStart= is the main process of the service. In this mode, if the process offers functionality to other processes on the system, its communication channels should be installed before the service is started up (e.g. sockets set up by systemd, via socket activation), as the service manager will immediately proceed starting follow-up units, right after creating the main service process, and before executing the service's binary. Note that this means systemctl start command lines for simple services will report success even if the service's binary cannot be invoked successfully (for example because the selected User= doesn't exist, or the service binary is missing).

The exec type is similar to simple, but the service manager will consider the unit started immediately after the main service binary has been executed. The service manager will delay starting of follow-up units until that point. (Or in other words: simple proceeds with further jobs right after fork() returns, while exec will not proceed before both fork() and execve() in the service process succeeded.) Note that this means systemctl start command lines for exec services will report failure when the service's binary cannot be invoked successfully (for example because the selected User= doesn't exist, or the service binary is missing).

I think it all boils down to change ping service to Type=exec.

Offline

#7 2021-10-11 21:11:58

zpg443
Member
Registered: 2016-12-03
Posts: 271

Re: [solved] Advice writing a systemd service unit

Offline

#8 2021-10-12 15:19:36

Ferdinand
Member
From: Norway
Registered: 2020-01-02
Posts: 331

Re: [solved] Advice writing a systemd service unit

Hm. To me it seems the foo.service requires that the ping-example-dot-com.service is active (is running, I assume), and will not start until after it has started.

I can't see, however, that foo.service in any way depends on a particular exit code from the command that is run in ping-example-dot-com.service?

A quick way to fix it might be to create a script for the ping-example-dot-com.service, that writes to a file in /tmp if the ping is successful, and then a corresponding script for foo.service, that will only execute /usr/bin/foo bar if that file exists. Or something like that smile

Offline

#9 2021-10-12 15:28:57

schard
Member
From: Hannover
Registered: 2016-05-06
Posts: 1,932
Website

Re: [solved] Advice writing a systemd service unit

If the ping command from the respective service exits with an error code, i.e. if all 15 pings did not succeed, the start of said unit *should* fail.
Hence, the dependent unit *should* not be started.
Please post your entire units in question, so that we can check them for possible errors.

Jason P. wrote:

I think it all boils down to change ping service to Type=exec.

That would certainly break things, since systemd will consider the unit up and runing immediately after exec'ing the ping binary and not wait for a possible nonzero return code.

Last edited by schard (2021-10-12 15:38:39)

Offline

#10 2021-10-13 09:20:58

Jason P.
Member
Registered: 2015-02-26
Posts: 171

Re: [solved] Advice writing a systemd service unit

Hi, thanks everyone for involving smile

@schard You're right. I misunderstood what Type=exec does. In fact is just a "safer" version of Type=simple but nothing else. If the command fails the subsequent units will not know because they are already started. So I ended up using Type=oneshot for the ping service and that's it. Tested and works as I need.

Offline

Board footer

Powered by FluxBB