Python script as a systemd service

Hi community,

I’m running a python script as a systemd service. The task of the script is extremely simple: check the last few lines of the file for changes. However, the problem is that for some reason, when the script is running as a systemd service, nothing happens, what I mean, is that the file is not read, and if there are any changes, the script does not react to it in any way. I would like to note that in the local environment everything works as it should, after running the script, the file is scanned for changes and if they are found, the action is performed.

Has anyone experienced this and can advise what the problem is?

In the script, I use the ‘while True’ loop. And here’s how the .service file looks like:

[Unit]
Description=Look for changes
After=multi-user.target

[Service]
Type=simple
Restart=always
Environment="FILE=/path/to/script/file.txt"
WorkingDirectory=/path/to/script
ExecStart=/usr/bin/python3 /path/to/script/script.py

[Install]
WantedBy=multi-user.target

If you use journalctl to look at the output from the script, do you see any errors or anything else unusual?

Hi Kevin,

Thank you for your reply! Not really, simple ‘else’ output. Here how my ‘while’ loop looks like:

timelas = 0

while True:

    timecur = os.path.getmtime(FILE)

    if timecur != timelas:

        for i in tail(FILE, 5):

            if INPUT.lower() in i.lower():
               
                subprocess.run(["echo", "new", "item"],)
                print("Check it 1")
          

            elif INPUT2.lower() in i.lower():

                subprocess.run(["echo", "new", "item2"],)
                print("Check it 2")
                
    else:
        print("Nothing new")

    timelas = timecur

    time.sleep(5)

The output:

INFO:__main__:Nothing new
INFO:__main__:Nothing new
INFO:__main__:Nothing new
INFO:__main__:Nothing new
INFO:__main__:Nothing new

The problem is that when there is some change in the file, the script does not seem to notice it and still outputs “INFO:main:Nothing new” to the log file.

OK, that at least confirms that the script is running.

I assume that’s not the entire script, because you are referring to FILE as a variable but never reading it from the environment. How does the script read the value of FILE?

Using ‘os’:

FILE = os.getenv('FILE')

Then I would suggest having the script output the timecur value that it is getting, to see what it says and compare it to the actual mtime of the file in question.

I will say that there are far more efficient ways to do this (systemd can even trigger a script based on changes to a file), but that’s a separate discussion from why your script doesn’t work at all :slight_smile:

There appears to be a lot left unshown in the script, but you could always resort to the simple but effective expedient of just adding print or logging calls to just see what is actually going on and isolate the actual problem—most importantly, of course, seeing what timecur and timelas [sic] are.

One idea out of left field, but since it apparently only happens when you’re running as a systemd service, perhaps it has to do with SELinux security settings not allowing access to the file, depending on where it is located (e.g. inside the /home directory? See python/devguide#583 However, you should see a journal message for that…

1 Like

Hi all,

Thank you for your replies! I’ve found the main cause of the issue. It was because service environment var (‘FILE’) was described in two places and it’s value was different:

  1. /etc/systemd/system/MY-SERVICE.service; (FILE=value1)
  2. /etc/systemd/system/MY-SERVICE.service.d/override.conf (a unit snippet override file); (FILE=value2)

I had to remove ‘Environment’ from ‘/etc/systemd/system/MY-SERVICE.service’ and keep it only in the ‘override.conf’ file.

1 Like

Glad you found it! In the future, when creating services like this, it’s really useful to emit a series of log messages at startup showing the configuration of the service. That allows you to catch misconfigurations like this more easily.

2 Likes

FWIW, I have my own PyPI package, serviceinstaller, to handle some of the nuts and bolts of systemd service installation, and is used by some of my other packages, but its pretty rudimentary right now so I’m not sure if its worth using at the moment. I believe there may be other such packages available, though none stood out to me as both maintained and having the necessary features at the time I ended up writing ServiceInstaller instead.

1 Like

Can you recommend a guide for constructing a robust python daemon with the accompanying systemd units?

I’m not aware of one, sorry, I’ve always just done it by hand.

2 thoughts:

  1. If you want you code to run as a daemon, I use pip install daemon.
    from daemon import basic_daemonize
    basic_daemonize()

  2. If you want to watch a file for changes look into inotify.