0

I've created the following systemd unit:

[Unit]
Description=MagicMirrorConfig
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=sh /home/pi/magicmirror-config/launcher.sh
Restart=always
User=pi
[Install]
WantedBy=multi-user.target

Note after looking at various examples of how to set this up, I've added both an After and Wants for network-online.target.

launcher.sh will start a Python script which attempts to send various MQTT messages.

Following a reboot if I check the systemctl journal I can see the following error:

Dec 12 12:53:21 magicmirror sh[640]: Traceback (most recent call last):
Dec 12 12:53:21 magicmirror sh[640]:   File "/home/pi/magicmirror-config/pir.py", line 37, in <module>
Dec 12 12:53:21 magicmirror sh[640]:     client.connect(broker)
Dec 12 12:53:21 magicmirror sh[640]:   File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 914, in connect
Dec 12 12:53:21 magicmirror sh[640]:     return self.reconnect()
Dec 12 12:53:21 magicmirror sh[640]:   File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 1044, in reconnect
Dec 12 12:53:21 magicmirror sh[640]:     sock = self._create_socket_connection()
Dec 12 12:53:21 magicmirror sh[640]:   File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 3685, in _create_socket_connection
Dec 12 12:53:21 magicmirror sh[640]:     return socket.create_connection(addr, timeout=self._connect_timeout, source_address=source)
Dec 12 12:53:21 magicmirror sh[640]:   File "/usr/lib/python3.9/socket.py", line 843, in create_connection
Dec 12 12:53:21 magicmirror sh[640]:     raise err
Dec 12 12:53:21 magicmirror sh[640]:   File "/usr/lib/python3.9/socket.py", line 831, in create_connection
Dec 12 12:53:21 magicmirror sh[640]:     sock.connect(sa)
Dec 12 12:53:21 magicmirror sh[640]: OSError: [Errno 101] Network is unreachable

This makes me think that the network is not available when my service starts.

Is there a way I can stop my service from starting until the network is available?

0

2 Answers 2

1

The simple way would be to just use a loop in the py script with a 5 or 10 second delay every time the connect fails, and limit that to enough iterations to cover a minute or two (or five, there's no harm done by this).

The issue with boot targets that are about a set of processes reaching a predefined state is that a protocol for such would cause more problems than it solves. If you read the man pages about Requires, After, Wants, etc. you'll notice these are primarily triggered by when a service starts. There is a little bit of refinement in that for some services systemd waits until the primary/first process of the service forks -- or finishes, if it is a short amount of time (think seconds).

This awkward process/service state protocol issue may not make sense at first, so to explain with the current example: If you have a service like dhcpcd which is responsible for setting up network connections, there's no protocol that systemd has whereby it can receive a signal indicating some state (eg. "wifi is now connected"). A good reason for that is that the service itself can incorporate a hook protocol of its own, independent of systemd's interface (which dhcpcd does), and having some form of adapter or whatever (keep in mind that dhcpcd was not written to be used exclusively with systemd) to bridge the two would almost certainly make the init system even more complex and cumbersome, without serving much purpose, because you can already use dhcpcd hooks to do whatever you want at various relatively fine grained points in the setup and connection of network interfaces (see man dhcpcd-run-hooks).

So I could explain how to do that, but I do not believe it is worthwhile in this case. If you are any good with shell scripting, you should be able to figure this out easily from that man page if you want -- but the internal loop I mentioned earlier is a more robust approach even if it isn't, on the surface, as satisfying as relying on a promise from the init system or the features of a specific networking daemon.

Another approach similiar to the loop idea is to just put a delay in before the script launches, using a systemd timer, cron, or a wrapper script. This is slightly less work than putting a loop in your python script, but I'd still recommend the latter as the external delay is "blind" in the sense that 30 seconds might work most of the time, but what if network connectivity gets delayed for several minutes? You could always make the delay much longer to cover that, but that will mean your service will always start five minutes (or whatever) after boot. Whereas a short loop, repeated for a longer amount of time, is both reasonably responsive and able to deal with delayed connectivity.

1
  • Thanks for your comments. As this is not a critical application I have delayed the start up of the python script to allow the network to come up.
    – pinoob
    Commented Jan 5, 2023 at 15:37
0

I have made it work on Raspberry pi model 2 in this way: [Unit] Wants=network-online.target After=network.target network-online.target

1
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Apr 30, 2023 at 17:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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