tgaryet
Posts: 17
Joined: Wed Apr 23, 2014 10:01 am

systemv init processing in jessie

Mon Nov 16, 2015 8:12 pm

I just switched to Raspian Jessie from Raspian Wheezy which changes the init processing to systemd.

I'm now having problems specifying the start/stop actions while switching run levels, particularly, from run level 5, to either 6 (reboot) or 0 (powerdown)

With Wheezy, as far as I understand it...

There is a set of rcx.d directories for each run level. Within a run level directory, there are links that point to each particular script to run while entering or exiting that run level.

When entering a run level, any link named with the first letter S is executed, passing the action of "start" to the script. When exiting the run level, any link staring with K is executed, with the action "stop".

Run levels 6 and 0, "exit" during reboot, so links here always start with K, and are passed "stop"

Using these rules, my scripts work fine in Wheezy.

The same scripts do not work in Jessie. In my specific case, the shutdown script is supposed to kill the power in run level 0, but no other run level. However, the script is getting called with "stop" in run level 5, even though it begins with an S, and there is no link starting with K.

I've made an example init script to show what I'm talking about:

Code: Select all

#!/bin/sh
# kFreeBSD do not accept scripts as interpreters, using #!/bin/sh and sourcing.
#if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then
#    set "$0" "[email protected]"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script
#fi
### BEGIN INIT INFO
# Provides:          StartStopCheck
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: StartStop Action Checker
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.  This example start a
#                    single forking daemon capable of writing a pid
#                    file.  To get other behavoirs, implemend
#                    do_start(), do_stop() or other functions to
#                    override the defaults in /lib/init/init-d-script.
### END INIT INFO

level=`runlevel`
/usr/bin/logger "StartStopCheck: $0 called with $1 at run level $level "

installing StartStopCheck with

update-rc.d StartStopCheck install

puts links like this in the rc.d directories:
[email protected] ~ $ ls /etc/rc*.d/*StartStopCheck
/etc/rc0.d/K01StartStopCheck /etc/rc2.d/S02StartStopCheck /etc/rc4.d/S02StartStopCheck /etc/rc6.d/K01StartStopCheck
/etc/rc1.d/K01StartStopCheck /etc/rc3.d/S02StartStopCheck /etc/rc5.d/S02StartStopCheck
A grep StartStop through syslog over a few shutdown's/reboots looks like this:
(shutdown)
Nov 16 13:06:27 raspberrypi systemd[1]: Stopping LSB: StartStop Action Checker...
Nov 16 13:06:28 raspberrypi logger: StartStopCheck: /etc/init.d/StartStopCheck called with stop at run level N 5
Nov 16 13:06:28 raspberrypi systemd[1]: Stopped LSB: StartStop Action Checker.
(reboot)
Nov 16 13:06:46 raspberrypi systemd[1]: Starting LSB: StartStop Action Checker...
Nov 16 13:06:46 raspberrypi logger: StartStopCheck: /etc/init.d/StartStopCheck called with start at run level unknown
Nov 16 13:06:46 raspberrypi systemd[1]: Started LSB: StartStop Action Checker.
The same StartStop script installed with the same update-rc.d command yields the same rcX.d link structure:
[email protected] ~ $ ls /etc/rc*.d/*StartStopCheck
/etc/rc0.d/K01StartStopCheck /etc/rc2.d/S02StartStopCheck /etc/rc4.d/S02StartStopCheck /etc/rc6.d/K01StartStopCheck
/etc/rc1.d/K01StartStopCheck /etc/rc3.d/S02StartStopCheck /etc/rc5.d/S02StartStopCheck
And this grep of syslog
Nov 16 19:42:20 raspberrypi logger: StartStopCheck: /etc/init.d/StartStopCheck called with stop at run level 2 6
Nov 16 19:42:43 raspberrypi logger: StartStopCheck: /etc/init.d/StartStopCheck called with start at run level N 2
I have a few questions:
1) is this the expected new behavior?
2) If so, what are the new rules?
3) Have the run levels changed (jessie ->5 on reboot vs 6 with reboot on Wheezy?)
4) Do my scripts now need to check OS version? What is the best way to handle it?

notro
Posts: 695
Joined: Tue Oct 16, 2012 6:21 pm
Location: Drammen, Norway

Re: systemv init processing in jessie

Wed Nov 18, 2015 12:31 am

I don't know much about systemd so I took this opportunity to have a closer look.
On boot systemd converts the init.d/rc scripts to systemd service units using systemd-sysv-generator.

Converted scripts have LSB in their description:

Code: Select all

~$ systemctl list-units --type=service | grep LSB
console-setup.service              loaded active exited  LSB: Set console font and keymap
dphys-swapfile.service             loaded active exited  LSB: Autogenerate and use a swap file
hdparm.service                     loaded active exited  LSB: Tune IDE hard disks
ifplugd.service                    loaded active exited  LSB: Brings up/down network automatically
kbd.service                        loaded active exited  LSB: Prepare console
keyboard-setup.service             loaded active exited  LSB: Set preliminary keymap
networking.service                 loaded active exited  LSB: Raise network interfaces.
ntp.service                        loaded active running LSB: Start NTP daemon
raspi-config.service               loaded active exited  LSB: Switch to ondemand cpu governor (unless shift key is pressed)
triggerhappy.service               loaded active running LSB: triggerhappy hotkey daemon
Let's look at triggerhappy:

Code: Select all

~$ systemctl show triggerhappy -p FragmentPath
FragmentPath=/run/systemd/generator.late/triggerhappy.service

~$ cat /run/systemd/generator.late/triggerhappy.service
# Automatically generated by systemd-sysv-generator

[Unit]
SourcePath=/etc/init.d/triggerhappy
Description=LSB: triggerhappy hotkey daemon
Before=runlevel2.target runlevel3.target runlevel4.target runlevel5.target shutdown.target
After=local-fs.target remote-fs.target
Conflicts=shutdown.target

[Service]
Type=forking
Restart=no
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=process
GuessMainPID=no
RemainAfterExit=yes
SysVStartPriority=1
ExecStart=/etc/init.d/triggerhappy start
ExecStop=/etc/init.d/triggerhappy stop
ExecReload=/etc/init.d/triggerhappy reload

~$ head /etc/init.d/triggerhappy
#!/bin/sh
### BEGIN INIT INFO
# Provides:          triggerhappy
# Required-Start:    $local_fs $remote_fs
# Required-Stop:     $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: triggerhappy hotkey daemon
### END INIT INFO
runlevel targets/aliases used in the converted service units:

Code: Select all

~$ ls -l /lib/systemd/system/runlevel*.target
lrwxrwxrwx 1 root root 15 Sep  5 22:23 /lib/systemd/system/runlevel0.target -> poweroff.target
lrwxrwxrwx 1 root root 13 Sep  5 22:23 /lib/systemd/system/runlevel1.target -> rescue.target
lrwxrwxrwx 1 root root 17 Sep  5 22:23 /lib/systemd/system/runlevel2.target -> multi-user.target
lrwxrwxrwx 1 root root 17 Sep  5 22:23 /lib/systemd/system/runlevel3.target -> multi-user.target
lrwxrwxrwx 1 root root 17 Sep  5 22:23 /lib/systemd/system/runlevel4.target -> multi-user.target
lrwxrwxrwx 1 root root 16 Sep  5 22:23 /lib/systemd/system/runlevel5.target -> graphical.target
lrwxrwxrwx 1 root root 13 Sep  5 22:23 /lib/systemd/system/runlevel6.target -> reboot.target
triggerhappy is started sometime between the targets basic and multi-user:

Code: Select all

~$ systemctl show triggerhappy -p Requires,Wants,WantedBy,Before,After
Requires=basic.target
Wants=system.slice
WantedBy=graphical.target multi-user.target
Before=graphical.target shutdown.target multi-user.target
After=local-fs.target remote-fs.target systemd-journald.socket basic.target system.slice
It is stopped on the shutdown target:

Code: Select all

~$ systemctl show triggerhappy -p Conflicts
Conflicts=shutdown.target
systemd doesn't use runlevels, it uses targets. So when you use the runlevel command in your script, you get a value corresponding to a systemd target.
runlevel reads /var/run/utmp, which is updated through:
/lib/systemd/system/systemd-update-utmp.service
/lib/systemd/system/systemd-update-utmp-runlevel.service

From all of this it looks like you should be able to continue using your init scripts as-is unless you rely on the runlevel value.

Links:
man bootup
How does systemd use /etc/init.d scripts?
man systemd.service
man systemd-sysv-generator
src/sysv-generator/sysv-generator.c

tgaryet
Posts: 17
Joined: Wed Apr 23, 2014 10:01 am

Re: systemv init processing in jessie

Thu Nov 19, 2015 3:03 pm

Notro, thanks for all the research you did to try to clarify. I wish I had the collection of links you made as a starting point.

In the hope this helps someone else, I going to try again, as there is a subtle(?) difference that I guess I did not make clear initially.

As notro describes, there are no runlevels, there are just targets. In Wheezy, with update-rc.d, you specify the runlevel(s) that init should pass "start" to the script, as well as possibly different runlevel(s) to pass "stop" to the script.

So the two lines in my StartStopCheck example:

# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6

create links names SxxStartStopCheck in rc2.d, rc3.d, rc4.d and rc5.d, and links named KxxStartStopCheck in directories rc0.d, rc1.d, and rc6.d.

These links are created identically in Jessie, but the systemd does not seem to follow the same *calling* rules as sys-v in Wheezy.

In sys-v (wheezy) when run level 2 is entered, StartStopChecck gets a "start" and does nothing happens when runlevel 2 is exited since there are no KxxStartStopCheck's in runlevel 2. If we are in runlevel 2 and issue a reboot (move to runlevle 6), KxxStartStopCheck doesn't get the "stop" until the xx'th step in the reboot process, when sys-v issues it's stop *exiting* runlevel 6.

By contrast, systemd issues a stop to StartStopCheck when it shuts down the current target even though there isn't a KxxStartStopCheck in rc5.d.

I made the StartStopCheck example for this discussion, but my the purpose of my actual script is to kills the power to the raspi. (http://redoakcanyon.com/power-controller-hat-with-a2d)

In Wheezy, killPower is set up to run just before halt, so rc0.d get set up something like:
.
.
K10killpower
K11halt

Since systemd issues a stop while leaving runlevel 5, it kills the power before shutting down lots of other things that get shut down after the LSB items are killed. This is not the desired behavior.

While debugging this problem, I found I had a script setup error that placed killPower in runlevels 2-5. In Wheezy it was benign since killPower only "started" in those levels, and killPower just exits when called with "start". I tried to remove any start level actions but update-rc.d screams at you for that.

Along the way, I tried setting up killPower as a service with a unit definition. I could never figure out the dependencies correctly. Since it has to stop as the last thing, it had to start as the first thing, but it could not, because it cannot start before, for example, the file system is up.

I did however, find this single line from man systemd-halt:

Immediately before executing the actual system halt/poweroff/reboot/kexec systemd-shutdown will run all executables in /lib/systemd/system-shutdown/
and pass one arguments to them: either "halt", "poweroff", "reboot" or "kexec", depending on the chosen action. All executables in this directory are
executed in parallel, and execution of the action is not continued before all executable's finished.
I put my script in this directory, with appropriate modifications for the argument decisions (kill power on poweroff, do nothing for the others), and it all worked great, although now I have to figure out how to install the script in the correct place based on the init system.

Terry

Return to “Advanced users”