23

I'm writing a shell script in bash. At some point in the script, it detects that the machine needs to be rebooted before continuing. It issues:

sudo reboot

When the machine comes back up, there is is more work that this script needs to do. How do I configure something to continue doing the work in this script?

I'm assuming that there is some place that I can write a shell script such that it will get executed at the next reboot. Where is such a place? I see that cron has an @reboot directive. I also know that services such as Apache are started on boot by upstart. Would either of those be appropriate mechanisms? If so, how would it be triggered?

This script only needs to run once, not every reboot. So it will have to go somewhere that just runs on the next reboot, or be able to remove itself once it has run.

This question asks about how to save your application state for after reboot. My script doesn't have much state, so I can manage that. I just need to know how to have this script trigger something to run after the next reboot.

My specific version is Ubuntu Linux 14.04. The original script is started on the command line by a system administrator (as opposed to running from cron).

Stephen Ostermiller
  • 982
  • 2
  • 11
  • 27
  • A reboot within a script should only be used if you CANNOT avoid it. For example a new kernel installation. I'm sure you can do your job without reboot. Can you specify why you need the reboot? – chaos Jul 18 '14 at 13:27
  • 1
    This script installs a new kernel (or rather calls apt upgrade which may do so). It also checks too see whether a reboot is actually needed before doing the reboot. – Stephen Ostermiller Jul 18 '14 at 13:32
  • "A reboot within a script should only be used if you CANNOT avoid it." -- @chaos Why? – John Red Nov 16 '16 at 05:16

1 Answers1

22

On a system, the only thing that is really persistent is a file. That's pretty much what you should use. Here's an solution using an init.d script.

Let's consider the following (simple) script, /etc/init.d/myupdate :

#! /bin/sh

### BEGIN INIT INFO
# Provides:          myupdate
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin

case "$1" in
    start)
        /path/to/update/script
        ;;
    stop|restart|reload)
        ;;
esac

If you activate it with update-rc.d myupdate defaults, the start action will be executed upon boot. Now, when your update script calls for a reboot:

touch /var/run/rebooting-for-updates
sudo reboot

With this solution, you can divide your update script into two parts :

before_reboot(){
    # Do stuff
}

after_reboot(){
    # Do stuff
}

if [ -f /var/run/rebooting-for-updates ]; then
    after_reboot
    rm /var/run/rebooting-for-updates
    update-rc.d myupdate remove
else
    before_reboot
    touch /var/run/rebooting-for-updates
    update-rc.d myupdate defaults
    sudo reboot
fi

It'll execute the before_reboot code section, create a file in /var/run, and reboot. Upon boot, the script will be called again, but since the file exists, after_reboot will be called instead of before_reboot.

Note that update-rc.d requires root privileges.

Without using a file (from Stephen Ostermiller's comment):

If you are familiar with the getopts utility, you may want to use options instead of files. In the init script, call the script with:

/path/to/update/script -r

And in your script, check for options instead of files. Call your script once without the option, and init.d will call it again on boot, this time with -r.

# Set AFTER_REBOOT according to options (-r).

if [ "x$AFTER_REBOOT" = "xyes" ]; then
    # After reboot
else
    # Before reboot
fi

You'll find more information about option handling here (for short options only). I also edited my script with calls to update-rc.d to keep this a one-time job (from another comment).

John WH Smith
  • 15,500
  • 6
  • 51
  • 62
  • 3
    It might be simpler to call `/path/to/update/script --after-reboot` from `/etc/init.d/myupdate` rather than relying on the presence of `/var/run/rebooting-for-updates`. Then it would have different arguments when run directly vs called at boot. – Stephen Ostermiller Jul 18 '14 at 13:50
  • Nice one, did not think about options. Allow me to edit ;) – John WH Smith Jul 18 '14 at 13:51
  • 1
    Also, since it is a one time job, I would probably want to add the `update-rc.d myupdate defaults` and `update-rc.d myupdate remove` into the script itself, as well as the writing and removal of `/etc/init.d/myupdate` so that it doesn't leave files sitting around. – Stephen Ostermiller Jul 18 '14 at 13:52
  • I've implemented this and it works well. The only other thing that I will add is that my Debian based system requires additional items in the `INIT INFO` section: https://wiki.debian.org/LSBInitScripts – Stephen Ostermiller Jul 19 '14 at 11:03
  • I really wanted to keep it simple ;) Writing proper init.d takes a little time, yet I'm glad you went for more information : these scripts can get pretty handy. – John WH Smith Jul 19 '14 at 13:25
  • Can you show a systemD example, since systemd is replacing init generally – FreeSoftwareServers Oct 03 '18 at 07:36