2

I'm writing some code to run a sound/light show from a raspberry pi 3 A+, using the latest Bullseye OS. I've been testing it manually, running via ssh, and all is well. However, it needs to run headless and unattended, so now I'm trying to turn it into a systemd service, and can't get it to start properly on reboot.

At first, I tried it as a system service. But because it uses pulseaudio, which runs in userland, it was failing to connect to pulse - even with the User= and Group= options set. After much googling, it seems that common wisdom is to not run pulse system-wide, and instead run the script as a user service. That's fine by me, as it makes the runtime environment more like my testing environment. (Raspi-config is set to auto-login my user.)

I installed it in .config/systemd/user, with the following unit file, and enabled it with systemctl --user enable lpr

[Unit]
Description=LPR Service
After=network-online.target

[Service]
Type=idle
ExecStart=/home/meeotch/test/mqlighting.py
WorkingDirectory=/home/meeotch/test
StandardInput=null
StandardOutput=file:/home/meeotch/logs/mqlighting_log.txt
StandardError=file:/home/meeotch/logs/mqlighting_log.txt
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

For testing, I stripped down the actual script to a minimal:

#! /usr/bin/env python

import sys, os
import csv
import subprocess

print("FOO", flush=True)
audio_dir = "/home/meeotch/audio"
silence = os.path.join(audio_dir, "750-milliseconds-of-silence.mp3")

while 1:
    pass

But when I reboot the pi, give it some time, then log in via ssh, I see the log file doesn't exist. And systemctl --user status lpr gives:

● lpr.service - LPR Service
     Loaded: loaded (/home/meeotch/.config/systemd/user/lpr.service; enabled; vendor preset: enabled)
     Active: inactive (dead)

grep -i lpr syslog shows nothing interesting. It's just dead, and never wrote anything to the log file. But systemctl --user start lpr works fine if I start it manually - the log file gets written, status shows as "running".

I've also tried with After=multi-user.target, but no love.

Any suggestions as to how I can figure out why it's dead, and not restarting on boot?

UPDATE: well, my several hours of googling seem to have not turned up a basic fact. From this thread: https://bbs.archlinux.org/viewtopic.php?id=250813 I discovered that neither multi-user.target nor network-online.target are user targets.

Swapped out the After= and WantedBy= to "default.target", and it seems to be working. However, it failed for another startup script that also uses networking (mqtt), with "OSError: [Errno 101] Network is unreachable".

So I guess now I have a follow-up question: the systemd docs say, "When a systemd user instance starts, it brings up the per user target default.target." It seems that this doesn't guarantee network services.

So what's the appropriate "late" target to use, when the service in question needs network, dbus, and other stuff? Ultimately, I'd like to turn off the GUI altogether, so graphical-session.target doesn't seem like a good choice.

UPDATE #2: So I discovered how to get a graphical representation with systemd-analyze plot > boot_analysis.svg, which was pretty helpful. The last few targets to run are: bluetooth, multi-user, and graphical. Since multi-user isn't available to a user service, and graphical doesn't run when I switch the boot to "Console with Login" (to disable the window manager and stuff I don't need), I chose bluetooth.target for the second service that was failing with "Network is unreachable". And now that service seems to work, too.

One weird thing about systemd-analyze plot is that it doesn't seem to show either of my services. I guess maybe it only shows system services, but also user-managed targets?

2
  • I'm not discouraging use of systemd, but I can't help but wonder if you could get this accomplished sooner/easier with a simple cron job?
    – Seamus
    Feb 12, 2023 at 21:21
  • Thx for the reply. In terms of figuring out how systemd works, probably yes. In terms of getting the extra systemd features "for free", maybe not? I assume I'd have to run the cron job every 1min or less, and have the script check for running instances, then quit if it's already running, or has exited cleanly due to a quit command - which status I'd read from a log file. I guess I just wanted to try doing it the "official" way for long-running proceses, with systemd - gaining the implied (but not necessarily true) stability that systemd supposedly provides.
    – meeotch
    Feb 13, 2023 at 2:46

2 Answers 2

1

As indicated in the updates to my original post, I've got a scheme that works. As described by user10489 in his comment below, there are two choices:

  1. auto-login user, use user services (possibly using enable-linger to prevent them from dying if the user gets logged out for some reason).
  2. use system services with User= set appropriately, including a service that starts a pulseaudio session for the user.

I'm going with #1 until something else breaks. Further info on #2 is available here: https://forums.raspberrypi.com/viewtopic.php?p=2080466#p2080466 https://forums.raspberrypi.com/viewtopic.php?p=2078308&hilit=pulseaudio#p2078290

0

You can get a list of possible targets with

systemctl list-units --type target --user

However, systemd probably isn't the best way to start things in a user session. It might be better to add it as a startup item in your desktop environment.

2
  • Thanks for the reply. And yeah, I got my list of user targets from the thread linked above, via ls /usr/lib/systemd/user/*.target - there's not a whole lot to choose from. But as mentioned, I eventually want to turn off the desktop GUI, and let this thing run headless & unattended. As a user startup script, I've got to roll my own logic for catching its exit codes, restarting it when appropriate, etc. - all the things that services are supposed to be for. I'd initially installed it as a system service, which felt more appropriate, it's the userland thing with pulse that prevented that.
    – meeotch
    Feb 12, 2023 at 16:46
  • There are two ways to handle this I think... You can set up the user to auto log in, or you can also start the sound services in your service.
    – user10489
    Feb 12, 2023 at 22:26

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.