Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 224537 - OpenRC should use dependency resolution to run shutdown and reboot scripts
Summary: OpenRC should use dependency resolution to run shutdown and reboot scripts
Status: RESOLVED FIXED
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: [OLD] baselayout (show other bugs)
Hardware: All Linux
: High normal (vote)
Assignee: Gentoo's Team for Core System packages
URL: http://www.apcupsd.com/manual/Shutown...
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-06-01 21:21 UTC by Matt Whitlock
Modified: 2008-12-24 15:09 UTC (History)
4 users (show)

See Also:
Package list:
Runtime testing required: ---


Attachments
rc.c.patch (rc.c.patch,682 bytes, patch)
2008-06-03 12:19 UTC, Matt Whitlock
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Matt Whitlock 2008-06-01 21:21:59 UTC
The baselayout shutdown scripts did this once upon a time, but perhaps it was not carried over in the move to OpenRC.

The following line should be prepended to /etc/init.d/shutdown.sh:

[[ -f /etc/apcupsd/powerfail ]] && /etc/apcupsd/apccontrol killpower

A brief explanation:  When apcupsd detects that the system needs to be shut down due to an exhausted battery backup during a power failure situation, it creates the file /etc/apcupsd/powerfail and executes 'shutdown -h now'.  The system shutdown scripts are expected to react to the presence of /etc/apcupsd/powerfail by calling '/etc/apcupsd/apccontrol killpower' immediately prior to the final call to halt, after file systems have been remounted read-only.  When this is set up properly, the system will halt and then after a short grace period the UPS will cut power.  When mains power is subsequently restored, the UPS will power back on, and if the BIOS is set up appropriately, the system will power back up.

If killpower is not called, the UPS will be drained all the way until it is depleted, which is not good for the batteries.
Comment 1 Roy Marples 2008-06-02 14:37:21 UTC
halt.sh had a little script to handle two UPS systems and these are probably Linux only. We need to have a generic way to call scripts just before final shutdown.

Maybe we should allow scripts to be added to the shutdown runlevel - but do we active the start or stop function? start I guess has the shutdown runlevel is starting. Then we could do this in halt.sh

rc shutdown

The trick would be that if we're in single user mode, then we run that shutdown scripts, otherwise we signal init to shutdown. This allows any shutdown scripts to work with dependency functions.
Comment 2 Matt Whitlock 2008-06-02 15:26:44 UTC
(In reply to comment #1)

Let me see if my understanding of this is correct.

1. My system is fully up and running (I'm in the 'default' runlevel), and I type poweroff.
2. poweroff is a symlink to /sbin/halt.
3. halt notices that I'm in init-runlevel 3, so it execs /sbin/shutdown. Also, since I invoked it as poweroff, it passes -P to shutdown.
4. shutdown sets INIT_HALT to 'POWEROFF' and signals init to switch to init-runlevel 0.
5. inittab has 'rc shutdown' in l0, so init starts that.
6. rc switches to the 'single' runlevel (thus stopping all services) and then calls /etc/init.d/halt.sh.
7. halt.sh sends SIGTERM and SIGKILL to any remaining processes, remounts remaining filesystems read-only, and then calls /etc/init.d/shutdown.sh.
8. shutdown.sh calls /sbin/halt, passing -p since INIT_HALT!='HALT'.
9. This time, halt sees that I'm in init-runlevel 0, so it tells the kernel to tell ACPI to turn off the system power.

My suggestion was to add the call to '/etc/apcupsd/apccontrol killpower' immediately before step 8.  However, you are right that there should be a more generic way to achieve this.

Your proposal, with which I wholeheartedly agree, should modify the above process thusly:

6. rc switches to the 'shutdown' runlevel, which does not implicitly include services in the 'boot' runlevel, so everything gets stopped.
7. The 'shutdown' OpenRC runlevel contains scripts for killing off processes, remounting remaining filesystems read-only, and calling /sbin/halt. These "services" get "started" as a result of entering the 'shutdown' OpenRC runlevel.
8. & 9. (deleted)

Then the apcupsd ebuild would only need to install its powerfail script in /etc/init.d and add it to the 'shutdown' runlevel.

Of course, there would also be a 'reboot' runlevel with parallel design for the case where the user invokes 'reboot' or 'shutdown -r'.
Comment 3 Matt Whitlock 2008-06-03 11:43:16 UTC
(In reply to comment #1)
> Then we could do this in halt.sh

I've been looking at rc.c, and you actually don't need halt.sh.  All the functionality of shutting down could be handled by scripts in the 'shutdown' and 'reboot' runlevels.

As an experiment, I've done the following:
1. Commented out the block in rc.c:main labeled "Run the halt script if needed."  That's lines 1215 through 1222 in openrc-0.2.5.
2. Converted '/etc/init.d/halt.sh' into a runscript script named '/etc/init.d/halt'.  The contents of the original halt.sh, minus the line labeled "Load the final script", are placed in the start() function.
3. Converted '/etc/init.d/reboot.sh' into a runscript script named '/etc/init.d/reboot'.  The contents of the original reboot.sh are placed in the start() function.  Additionally, reboot has 'need halt' in its depend().
4. Created a new directory '/etc/runlevels/reboot'.
5. Executed 'rc-update add reboot reboot'.

If all goes as I expect, when I execute 'reboot' or 'shutdown -r' or 'rc reboot', the following should occur:
6. I should get switched into init runlevel 6.
7. All of my services should get stopped.
8. Dependency resolution for the 'reboot' runlevel should determine that 'halt' needs to start before 'reboot'.
9. 'halt' should start, which will do what halt.sh usually does.
10. 'reboot' should start, which will do what reboot.sh usually does.

Then if this all works, I'll convert '/etc/init.d/shutdown.sh' to a runscript script named '/etc/init.d/shutdown', create a '/etc/init.d/powerfail' script for apcupsd, create directory '/etc/runlevels/shutdown', and add 'powerfail' and 'shutdown' to the 'shutdown' runlevel.
Comment 4 Matt Whitlock 2008-06-03 12:19:32 UTC
Created attachment 155351 [details, diff]
rc.c.patch

I had to make one other change to rc.c so that it would calculate the service start order for the 'single', 'shutdown', and 'reboot' runlevels.  See the attached patch.
Comment 5 Matt Whitlock 2008-06-03 12:25:51 UTC
Interesting results.  It very nearly worked.  It did the remounting of my filesystem as read-only but then at the very last moment, it got tripped up and gave this error:

 * WARNING: halt not under our control, aborting
INIT: no more processes left in this runlevel

So for some reason, '/sbin/reboot -dpk' refused to do its thing, even though init was in runlevel 6.  Ideas/comments?
Comment 6 Matt Whitlock 2008-06-03 12:40:16 UTC
Changed the summary line to better reflect what this is all about.
Comment 7 Roy Marples 2008-06-03 15:24:38 UTC
Try removing the killall5 commands from halt
Comment 8 Matt Whitlock 2008-06-03 15:37:35 UTC
(In reply to comment #7)
> Try removing the killall5 commands from halt

Oh, I bet you're right.  The difference in the way I'm shutting down now is that the final script is being forked from rc rather than replacing the rc process by means of exec.  I'll try commenting out those killall5 commands in the halt script and see what happens.

Incidentally, I renamed the scripts more appropriately:
/etc/init.d/shutdown : has contents of old halt.sh
/etc/init.d/halt : has contents of old shutdown.sh; 'need shutdown'
/etc/init.d/reboot : has contents of old reboot.sh; 'need shutdown'
'shutdown' runlevel contains: halt
'reboot' runlevel contains: reboot

I am thinking the apcupsd powerfail script should have 'after shutdown' and 'before halt' in its depend() section and be added to the 'shutdown' runlevel.
Comment 9 Matt Whitlock 2008-06-03 16:52:03 UTC
Commenting out the killall5 commands didn't work.

And I realized I had misinterpreted the error message.  It's not that /sbin/reboot is refusing to work but rather that runscript is bailing out of /etc/init.d/reboot after starting 'shutdown'.  I guess runscript thinks it's not "in_control" and so does not proceed with executing reboot's start() function after having started 'shutdown' to satisfy reboot's 'need'.
Comment 10 Doug Goldstein (RETIRED) gentoo-dev 2008-10-07 15:17:32 UTC
According to Roy, this may be fixed in 0.3.0, so we need a test and some results back.
Comment 11 Matt Whitlock 2008-10-08 02:46:04 UTC
Tried again with OpenRC 0.3.0.

The system is going down for reboot NOW!
INIT: Switching to runlevel: 6
INIT: Sending processes the TERM signal
INIT: Sending processes the KILL signal
 * Caching service dependencies ...                         [ ok ]
 * Saving random seed ...                                   [ ok ]
 * Deactivating swap devices ...                            [ ok ]
 * Unmounting loopback devices
 * Unmounting filesystems
 *   Unmounting /mnt/flash ...                              [ ok ]
 * Setting hardware clock using the system clock [UTC] ...  [ ok ]
 * Setting system clock using the hardware clock [UTC] ...  [ ok ]
 * Remounting remaining filesystems read-only ...
 *   Remounting / ...                                       [ ok ]
 * WARNING: shutdown not under our control, aborting
INIT: no more processes left in this runlevel

The result is the same regardless of whether /etc/init.d/shutdown has the killall5 commands or they are commented out.

It appears that rc is correctly resolving the 'reboot' runlevel and executing the 'reboot' service script.  runscript correctly determines that the 'reboot' service needs 'shutdown', so it starts the 'shutdown' service, which correctly unmounts/remounts file systems.  But then when the 'shutdown' start function exits (exit 0), runscript panics for some reason and doesn't continue with starting the 'reboot' service.
Comment 12 Roy Marples 2008-10-10 09:55:17 UTC
My new train of though is that you should revert your changes as it just won't work :)

As halt.sh has unmounted /lib/rc/init.d AND made / ro by this point, we cannot easily run dependencies so it will have to be a lexical ordering.

I think halt.sh could do something like this.

for x in /etc/init.halt.d/*; do
    [ -x "${x}" ] && "${x}"
done

And UPS systems can wang their script in there.

Thoughts?
Comment 13 Matt Whitlock 2008-10-10 10:52:49 UTC
I agree with your conclusion, but I think you're abandoning too much of the train of thought.  The dependency resolution works.  The problem is that runscript can't continue after the svcdir has been unmounted.  Therefore, we only have to ensure that the very last "service" to "start" during shutdown is the one that unmounts this dir.

For example, we could have 'halt' and 'reboot' service scripts that source shutdown.sh (which would do all the re/unmounting) before doing their final command to power off or reboot the system.  Then other scripts like the UPS thing would just be 'before halt reboot'.

I'm going to try this right now.
Comment 14 Matt Whitlock 2008-10-10 11:20:03 UTC
/etc/runlevels/shutdown/halt@ -> /etc/init.d/halt*
/etc/runlevels/reboot/reboot@ -> /etc/init.d/reboot*

-- /etc/init.d/halt ------------
depend() {
        return 0
}

start() {
        source /etc/init.d/shutdown.sh

        opts="-d"
        [ "${INIT_HALT}" != "HALT" ] && opts="${opts}p"
        [ "${RC_DOWN_INTERFACE}" = "yes" ] && opts="${opts}i"
        [ "${RC_DOWN_HARDDISK}" = "yes" ] && opts="${opts}h"

        /sbin/halt "${opts}"

        # hmm, if the above failed, that's kind of odd ...
        # so let's force a halt
        /sbin/halt -f
}
----------------

-- /etc/init.d/reboot ------------
depend() {
        return 0
}

start() {
        source /etc/init.d/shutdown.sh

        opts="-dpk"
        [ "${RC_DOWN_INTERFACE}" = "yes" ] && opts="${opts}i"

        /sbin/reboot "${opts}" 2>/dev/null

        # hmm, if the above failed, that's kind of odd ...
        # so let's force a reboot
        /sbin/reboot -f
}
----------------

--- halt.sh     2008-10-10 06:55:48.185251000 -0400
+++ shutdown.sh 2008-10-10 07:02:50.381019652 -0400
@@ -111,9 +111,3 @@
        [ -x /sbin/sulogin ] && sulogin -t 10 /dev/console
        exit 1
 fi
-
-# Load the final script - not needed on BSD so they should not exist
-[ -e /etc/init.d/"$1".sh ] && . /etc/init.d/"$1".sh
-
-# Always exit 0 here
-exit 0


Just as a point of order, I'm still relabeling these scripts to reflect the fact that the system first shuts down (by shutdown.sh) and then either reboots or halts.  It makes more sense than halting first and then either rebooting or shutting down: if the system has halted, it cannot do anything further, even so much as to reboot.

Unfortunately I cannot test this setup at the moment, as my system is performing its scheduled incremental backup across the Internet, and today happens to be a rather complete dump day, so it's going to take a couple more hours before it finishes.  I'll comment again when I have some results to share.
Comment 15 Matt Whitlock 2008-10-10 12:34:47 UTC
Begging your pardon, it just dawned on me that this isn't helpful because it's not solving the problem I initially set out to solve, which is to have apccontrol run AFTER un/remounting file systems but before halting the system.  Everything I just suggested could just as effectively be done by having a no-op service start in the boot runlevel and do its work in its stop() function.

If the svcdir is a tmpfs, why does it get unmounted at all?  Doesn't matter if it's still mounted rw when the system powers off.  If that could remain mounted, we might not run into the "out of our control" error.
Comment 16 Roy Marples 2008-10-10 13:31:15 UTC
(In reply to comment #15)
> If the svcdir is a tmpfs, why does it get unmounted at all?  Doesn't matter if
> it's still mounted rw when the system powers off.

So we can save the deptree and nettree to disk and reload them next time.
Otherwise we would have to rebuild them at boot time which may or may not be possible if code in 3rd party scripts references things off /.

We need to save them to the same area of the disk as where the tmpfs mount is as we have to work without tmpfs or similar too. This is because some systems doesn't have any ramdisk capacity or ability to mount/umount. Also, some systems lack the --bind mount option which would be needed to otherwise facilite this.

Of course I could be talking a load of nonsence as I've just gotten back from a beery lunch :) Feel free to provide counter arguments.

> If that could remain
> mounted, we might not run into the "out of our control" error.

What's the exact error you're referring to?
Comment 17 Matt Whitlock 2008-10-10 14:11:46 UTC
(In reply to comment #16)
> What's the exact error you're referring to?

In runscript.c, if in_control() returns false, then svc_start prints the warning "WARNING: %s not under our control, aborting" on line 856 and aborts.

There are five tests in in_control() that must pass in order for svc_start to continue.  The shenanigans performed by the stock halt.sh script to re-finagle the svcdir are somehow tripping up one of these five tests.
Comment 18 Roy Marples 2008-11-03 15:45:05 UTC
OK, we now allow scripts to be run in the shutdown runlevel with full dependencies. The reboot runlevel has been removed, but will be mapped to the shutdown runlevel.

OpenRC should never interact with the init system, instead the init system should call OpenRC. Thus the provided halt script can now be safely moved to the sysvinit package, but it's here for the time being.

http://git.overlays.gentoo.org/gitweb/?p=proj/openrc.git;a=commitdiff;h=6e17299e1c681d63bedd1b2f796e7a30568de172

You should ensure you have the masked version of sys-fs/udev-130-r2 before continuing. You may also have to manually add the shutdown runlevel and add the following scripts to it: halt, savecache, romount, killprocs.
Comment 19 Matt Whitlock 2008-11-03 15:52:54 UTC
Whoa, awesome, Roy!  You really came out of nowhere with this.  I look forward to trying it out and contributing a powerfail shutdown script to the apcupsd ebuild.
Comment 20 Roy Marples 2008-11-04 11:45:14 UTC
romount has been moved to mount-ro
Comment 21 Matt Whitlock 2008-12-01 21:58:36 UTC
Roy, I'm eager to try this out.  When will this feature release hit Portage?  Do you need my help testing it prior to releasing it?  I could possibly check out development code if you tell me what to get and where to get it.  Otherwise, I will wait patiently.  Thank you for your work.
Comment 22 Roy Marples 2008-12-02 00:26:01 UTC
Futher releases depend on bug #246502 being resolved :/
Comment 23 Doug Goldstein (RETIRED) gentoo-dev 2008-12-24 15:09:26 UTC
This is fixed with OpenRC 0.4.0, which is available in the tree.