If you’re running your Raspberry Pi in a headless mode, sometimes it’s hard to know when shutdown is complete and it’s safe to turn off the power. If you have a PiFace you could use that to display a message when shutdown is complete. In this article we’ll see how to do just that.
First of all, let’s create a python script to display a message on the PiFace. Check out my previous post on getting started with the PiFace for more details on exactly how to do this.
This is the script I’ll be using, it displays the Swindon Hackspace logo and a simple message.
Test that your script works by running it now:
Getting the Script to Run on Shutdown
The Linux rc system controls what services and processes run at boot time and at shutdown. It’s fairly easy to insert a small script into the startup sequence because you can simply add it to
rc.local, however there is no equivalent for shutdown.
We will need to create ourselves a fully fledged init script. This will live in
/etc/init.d/. If you have installed the PiFace package on your Raspberry Pi you should already have one in there called
pifacecadsysinfo which you can configure to run at startup to display system info (CPU temperatures, etc). This will be a good starting point for our shutdown script. Make a copy of it and call it
Open it up in your favourite editor, since it is in the
etc folder you’ll need to be root to be allowed to save changes, so I use this:
Looking at the structure of the script there are a few points of interest. The header at the top defines some parameters used by the init system to determine script order, more on that later.
For now I’m interested in the start and stop functions. The
stop() function is where we need to call our script. We can take the code currently in the
start() function and use that as a basis of our
stop() function. We don’t need all that stuff about tracking the process identifier so just copy the call to python.
Make sure to change the value of the
SCRIPT_FILE variable towards the start of the script to point to wherever you saved your python script. I saved mine in the home directory under a
piface folder. You’ll need to specify the full path here.
We don’t want to do anything on start in this script (we could display a startup message of course), so clear out that method and just replace it with a friendly message that will be printed out to the console.
The Tricky Bit
The whole idea of this is to try and give us an indication that it’s safe to disconnect the power, so we want our script to run as late as possible in the shutdown sequence.
Linux startup and shutdown is controlled by scripts which are run as part of run-levels. There is more info here but for now all we need to know is that runlevel zero specifies scripts to run at shutdown. The scripts live in
/etc/init.d and from there they are linked to directories
/etc/rc1.d, etc, one for each run-level.
We can see which scripts are currently configured to run at shutdown by looking in
/etc/rc0.d. These scripts will be run in order when the system shuts down. ‘K’ means kill and the two digit number is used to provide the order. For scripts with the same number it isn’t really defined what the exact order is, the implication being that it isn’t really important. Some Linux init systems will try to run scripts in parallel in order to speed up the process.
It looks pretty easy to add our script using the
We need to give it the name of our script
<basename>, what we want to do (start or stop), the number to run as (NN) and the run-level to add it to. This should work then, we’ll choose
NN = 99 to make sure it runs last.
Unfortunately it seems to have ignored the
NN parameter and puts our script fairly early on in the sequence:
This probably wouldn’t matter for many scripts, but we want this to run as near to last as possible so that once the message is displayed we know it’s okay to disconnect the power. So we’ll have to do something else.
Notice the output we got from
update-rc.d, it said “using dependency based boot sequencing”. That header in the init script we saw earlier is important. It looks a bit like a big comment (‘#’ is used to indicate a line is a comment in many linux scripts and some programming languages).
Looks like we’d better update this. First off, it would be polite to update the descriptions, so do that.
Default-Stop look like they give a hint to which run-levels our script should be in by default, so let’s clear out
Default-Start and set
Default-Stop to just include run level zero (shutdown). Note that run-level one is single-user mode, generally only used by sysadmins for doing system maintenance or when you have a serious problem, and run-level six is used for reboot.
The remaining three parameters look like they have something to do with the order in which the scripts are run.
Required-Stop look like they control the order of starting and stopping services. We aren’t fussed about start-up so clear this out.
At this point I’m going to skip a bit of trial and error I did while I played around with various values in
Required-Stop option, but let’s just say that nothing I tried made much difference. The values you can use in this option are the name of another init script, or a few special values (such as
$syslog) which are defined in the documentation.
Eventually I abandoned trying to make
Required-Stop work and noticed an option that wasn’t in the original script we copied called
X-Stop-After. This makes much more sense and does what it says on the tin – stop this script after some other.
Looking at the scripts that run in our current shutdown sequence (above) the last few are
K10halt. We can’t stop after
halt because the system will be halted at that time. I tried stopping after
unmountroot which nearly worked, but it didn’t quite have time to complete drawing the entire display before the system halted so I ended up with only half a message.
Finally I decided on
X-Stop-After: umountfs, which seemed to work nicely. You may need to experiment a little. My header ended up looking like this:
Finally, re-add the script to run-level zero and check it worked.
Much better! Make sure to clear the PiFace if it’s still showing the shutdown message from our earlier test (or you won’t know if it’s worked or not), then shutdown your Raspberry Pi. It doesn’t matter how you shutdown, I usually use the command line
sudo shutdown now -h.