Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pygame.init() fails when run with systemd

I'm trying to run a python pygame script with systemd and for some reason the script just exits without an error. This is on a Raspberry Pi with Raspian "Jessie lite".

If I run the script manually with "sudo python myscript.py" it works fine.

sudo systemctl status myscript.service reports:

* myscript.service - Python Script
Loaded: loaded    /etc/systemd/system/myscript.service; enabled)    Active: inactive (dead) since Mon 2016-08-29 04:33:19 UTC; 1s ago
Process: 3275> ExecStart=/usr/bin/python /home/pi/myscript.py (code=killed, signal=HUP)
Main PID: 3275 (code=killed, signal=HUP)

If I start the service manually with sudo systemctl start myscript.service the same thing happens.

I've stripped down my script to just the pygame.init() call. This is where it exits.

If I try to initialize the modules manually then "cdrom", "joystick", "threads", and "font" initialize normally but a call to display.init() causes the program to exit. There is no exception raised.

The only resource I could find online is this guy. It seems he ran into the exact same thing I'm seeing. I've tried strace and if I wait long enough (2 minutes), it will work! Obviously I can't run with strace all the time. I think it slows down the execution of the initialization to somehow allow it to work.

EDIT: So the issue appears to be systemd sending a SIGHUP. If this is unhandled in Python the default action is to exit. A quick fix is the catch SIGHUP:

import signal
def handler(signum, frame):
    pass

try:
    signal.signal(signal.SIGHUP, handler)
except AttributeError:
    # Windows compatibility
    pass

So many burning questions. Why does systemd do this? Why does strace fix the issue? Why do some Python scripts get SIGHUP while others don't?

like image 207
user5541269 Avatar asked Oct 30 '22 21:10

user5541269


2 Answers

I don't have an answer, but I'm experiencing the same thing, so I'll add some more detail.

Here's the cut-down code:

#!/usr/bin/python2.7

import logging
from pygame import display
import signal
import time

def handler(signum, frame):
  """Why is systemd sending sighups? I DON'T KNOW."""
  logging.warning("Got a {} signal. Doing nothing".format(signum))

signal.signal(signal.SIGHUP, handler)
signal.signal(signal.SIGTERM, handler)
signal.signal(signal.SIGCONT, handler)

logging.warning("About to start display.")
try:
  display.init()   # hups
except Exception as ex:
  logging.warning("Got any exception: %s " % ex)

logging.warning("Quitting in 60")
time.sleep(60)

Here's the log that produces:

Jul  8 22:30:27 beardog systemd[1]: Started PyGame Test.
Jul  8 22:30:27 beardog pygame[17406]: WARNING:root:About to start display.
Jul  8 22:30:27 beardog pygame[17406]: WARNING:root:Got a 1 signal. Doing nothing
Jul  8 22:30:27 beardog pygame[17406]: WARNING:root:Got a 18 signal. Doing nothing
Jul  8 22:30:27 beardog pygame[17406]: WARNING:root:Quitting in 60

It's getting a SIGCONT immediately after the SIGHUP, but no SIGTERM. Systemd claims to only ever send a SIGHUP after a SIGTERM, so maybe it's coming from somewhere else? I can't find anything relevant in the pygame code though.

I've turned on systemd debug logging, but it doesn't print anything interesting.

Here's my systemd config.

[Unit]
Description=PyGame Test
After=syslog.target network.target network-online.target graphical.target

[Service]
Type=simple
WorkingDirectory=/path/to/code/pygame/
ExecStart=/path/to/code/pygame/why.py
Restart=always
RestartSec=5
LimitNOFILE=10000
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=pygame
SendSIGHUP=no

[Install]
WantedBy=multi-user.target

I've tried this as Type=forking, oneshot and dbus (although it isn't any of them). I've also tried TimeoutStartSec=20, but no change. Tested on an Ubuntu Xenial laptop and on a Raspberry pi running Raspbian. Both python2.7 and python3. Code works fine when run manually, and seems to work when run under strace in systemd. /o\

Like the OP, I can work around it by catching the SIGHUP, but after this much debugging I'd love to know what's going on.

like image 166
cartographer Avatar answered Nov 02 '22 10:11

cartographer


For me the solution was to only init specific modules which I needed.

In my case instead pygame.init() I initialized only pygame.mixer.init() and now systemd can start the service.

like image 25
Tamas Toth Avatar answered Nov 02 '22 10:11

Tamas Toth