To launch a script at startup a proper service file have to be created and added to the systemd.
Let's create a service file for our blik.py program. Create as root a file called blink.service in /lib/systemd/system directory.
sudo nano /lib/systemd/system/blink.service
then save inside it this content:
[Unit] Description=Blinking led After=network.target [Service] Type=idle ExecStart=/usr/bin/python /home/pi/blink.py Restart=always User=pi [Install] WantedBy=multi-user.target
Next systemd has to be configured, enabling the service so at the next reboot it will be started automatically. First off, though, the systemd itself has to be reloaded in order to detect the new service definition:
sudo systemctl daemon-reload
Next, enable the service
sudo systemctl enable blink
Check if it is enabled:
sudo systemctl list-unit-files | grep enabled
Of course, it can be started manually just now:
sudo systemctl start blink
The PID (process id) can be now checked:
ps -ef | grep blink pi 817 1 0 09:19 ? 00:00:00 /usr/bin/python blink.py
it is 817 (the number will be different in your case). Please note that it has been started from systemd process, in fact the PPID (parent process id) is 1.
In order to simulate a crash, we kill it manually:
sudo kill 817
Now we can check it was restarted looking again to the PID (process id):
ps -ef | grep blink pi 1037 1 0 09:19 ? 00:00:00 /usr/bin/python blink.py
it is now 1037 (PPID always 1), so, as it is different, that means has been restarted automatically after the kill.
To check what is doing your service launch this command:
journalctl -f blink
It is possible, of course, to add our own messages to this log. In Python, for example, install the python-systemd package:
sudo apt-get update sudo apt-get install python-systemd
add these lines to your code:
import logging from systemd.journal import JournalHandler log = logging.getLogger('blonk') log.addHandler(JournalHandler()) log.setLevel(logging.INFO) log.info("Blinking led started")
Open another terminal and launc journalctl with follow option (-f):
journalctl -u -f blink ... May 13 11:49:24 roadrunner systemd: Started Blinking led. May 13 11:49:25 roadrunner /home/pi/blink.py: Blinking led started
Create a systemd service file in /lib/systemd/system/blink.service with this contents:
[Unit] Description=Blinking led [Service] WorkingDirectory=/home/pi ExecStart=python blink.py User=pi
Create a systemd timer file for this service in /lib/systemd/system/blink.timer with this contents::
[Unit] Description=Runs blink.py every hour [Timer] # Time to wait after booting before we run first time OnBootSec=10min # Time between running each consecutive time OnUnitActiveSec=1h Unit=blink.service [Install] WantedBy=multi-user.target
Reload the service definitions:
sudo systemctl daemon-reload
Then is your are using the same file of previous example disable the blink.service and enable just the blink.timer to start it after the bootstrap:
sudo systemctl disable blink.service sudo systemctl enable blink.timer
Let's try it now:
sudo systemctl start blink.timer
[Unit] Description=Runs blink.py daily at midnight [Timer] OnCalendar=*-*-* 00:00:00 Persistent=True Unit=blink.service [Install] WantedBy=multi-user.target
Changing the OnCalendar parameter is possible to start the blink.service in a lot of different way:
Minimal form Normalized form Sat,Thu,Mon-Wed,Sat-Sun ==> Mon-Thu,Sat,Sun *-*-* 00:00:00 Mon,Sun 12-*-* 2,1:23 ==> Mon,Sun 2012-*-* 01,02:23:00 Wed *-1 ==> Wed *-*-01 00:00:00 Wed-Wed,Wed *-1 ==> Wed *-*-01 00:00:00 Wed, 17:48 ==> Wed *-*-* 17:48:00 Wed-Sat,Tue 12-10-15 1:2:3 ==> Tue-Sat 2012-10-15 01:02:03 *-*-7 0:0:0 ==> *-*-07 00:00:00 10-15 ==> *-10-15 00:00:00 monday *-12-* 17:00 ==> Mon *-12-* 17:00:00 Mon,Fri *-*-3,1,2 *:30:45 ==> Mon,Fri *-*-01,02,03 *:30:45 12,14,13,12:20,10,30 ==> *-*-* 12,13,14:10,20,30:00 mon,fri *-1/2-1,3 *:30:45 ==> Mon,Fri *-01/2-01,03 *:30:45 03-05 08:05:40 ==> *-03-05 08:05:40 08:05:40 ==> *-*-* 08:05:40 05:40 ==> *-*-* 05:40:00 Sat,Sun 12-05 08:05:40 ==> Sat,Sun *-12-05 08:05:40 Sat,Sun 08:05:40 ==> Sat,Sun *-*-* 08:05:40 2003-03-05 05:40 ==> 2003-03-05 05:40:00 2003-03-05 ==> 2003-03-05 00:00:00 03-05 ==> *-03-05 00:00:00 hourly ==> *-*-* *:00:00 daily ==> *-*-* 00:00:00 monthly ==> *-*-01 00:00:00 weekly ==> Mon *-*-* 00:00:00 *:20/15 ==> *-*-* *:20/15:00
systemctl list-timers --all ... NEXT LEFT LAST PASSED UNIT ACTIVATES Mon 2018-05-14 00:00:00 CEST 10h left Sun 2018-05-13 13:14:44 CEST 2min 17s ago blink.timer blink.service Mon 2018-05-14 10:38:28 CEST 21h left Sun 2018-05-13 10:38:28 CEST 2h 38min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service n/a n/a n/a n/a apt-daily-upgrade.timer apt-daily-upgrade.service n/a n/a n/a n/a apt-daily.timer apt-daily.service
This is an example on how to start a Python script called shutdown.py at shutdown:
[Unit] Description=Send a message on the Oled at shutdown [Service] Type=oneshot RemainAfterExit=true WorkingDirectory=/home/pi/Adafruit_Python_SSD1306/examples ExecStop=/usr/bin/python shutdown.py User=pi [Install] WantedBy=multi-user.target
Save is as /lib/systemd/system/oled.service
Reload the new service definition:
sudo systemctl daemon-reload
Enable the new service:
sudo systemctl enable oled.service
A few pieces of old way are still present and working as for example /etc/rc.local where all the applications that implement the system functionality are started.
A typical rc.local contains:
cat /etc/rc.local #!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. cd /home/acmesystems /usr/bin/nohup /usr/sbin/python blink.py & exit 0
In this example a Python program is started from application's home directory, and left in background (using the trailing ampersand). That works passably well till our python application never crashes: in such unfortunate case, our system only chance has, in order to regain the lost functionality, is to be manually restarted by user (probably cycling the power...).
Usually, the quick and dirty solution suggested is to add our application to the /etc/inittab file, specifyng a respawn in the line:
in such case startup must be a proper script able to start out application (and probably it could be convenient to reuse this script as is even in /etc/rc.local).
2018 Ⓒ TanzoLab