I put a python script with an infinite loop into /etc/rc.local but the machine boots successfully, which confuses me.

The /etc/rc.local content:

#!/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.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"

/home/pi/py/startsignal.py &
touch /home/pi/thisisrun
exit 0


import RPi.GPIO as GPIO

GPIO.setup(18, GPIO.OUT)

GPIO.output(18, 1)


# coding: utf8
import RPi.GPIO as gpio

upper_temp = 55
lower_temp = 45
# minutes
check_interval = 2

def get_temp():
    with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
        temp = float(f.read()) / 1000
    return temp

def check_temp():
    if get_temp() > upper_temp:
        gpio.setup(23, gpio.OUT)
    elif get_temp() < lower_temp:
        gpio.setup(23, gpio.IN)

if __name__ == '__main__':
    # check every 2 minutes
        while True:
            sleep(check_interval * 60)

All the code relevant is above. I thought about this after googling around.

  1. the #!/bin/sh -e indicates that the script will exit once an error occurs.
  2. the /home/pi/thisisrun file is not created, so there must be an error above this line
  3. after booting into system I can see that fan.py is running. So I guess the error occurs during the execution of fan.py. But the fan.py has an infinite loop in it !

How can a python script generates an error but still runs normally?

How can /bin/sh detects the error when the fan.py never returns?

OS: raspbian stretch


Assuming that Raspbian Stretch uses systemd like regular Debian Stretch does by default, /etc/rc.local is started by /lib/systemd/system/rc-local.service:

#  This file is part of systemd.
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.local is executable.
Description=/etc/rc.local Compatibility

ExecStart=/etc/rc.local start

As it specifies Type=forking, TimeoutSec=0 and RemainAfterExit=yes, I understand that systemd basically starts it and does not care whether it exits or not. So that explains why the system can successfully complete the boot even if /etc/rc.local is still running.

Your rc.local script first runs startsignal.py in the background (= with the &): that means only a failure to start the script would cause an error in the rc.local script at that point. If startsignal.py successfully starts but then returns an error, rc.local would have to use wait <process or job ID> to read the incoming error from the startsignal.py process. But your process apparently does not care to check for that.

Then your rc.local starts fan.py. Since it is started without &, the shell will start another process to run fan.py and wait for it to exit... but since fan.py has an infinite loop, it will not exit until the system is being shut down, fan.py has an error, or the process running fan.py is killed. The touch /home/pi/thisisrun will be executed only after fan.py has exited.

I think it would make more sense to start startsignal.py without the & and fan.py with it, rather than vice versa.

  • I did an experiment in CentOS7 by putting a py script (foreground, also has an infinite loop in it) in rc.local and the rc-local service is the same except the dir of rc.local is /etc/rc.d/rc.local. This time the system won't boot. Both CentOS7 and Stretch use systemd use systemd and the same config. Care to explain this? – z.h. May 7 '18 at 10:12
  • OK, then please add set -x as the first non-comment line in rc.local. Then you'll see what actually gets executed by the script, either as on-screen messages at boot time, or with journalctl _SYSTEMD_UNIT=rc-local.service later. Also note that Debian has a drop-in file that modifies the rc-local.service: use systemctl cat rc-local.service to see the full service definition that is actually in effect. – telcoM May 7 '18 at 13:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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