64

I want to execute a simple command just before the computer shuts down (timing is not essential).

For startup, I can use /etc/rc.local; is there something similar for shutdown?

Note that I would still like to use the integrated shutdown button from menu; i.e. I don't want to use a custom script every time I shutdown via terminal - it needs to be automatic.

1
  • In OpenSuSE /etc/init.d/halt.local "... script with local commands to be executed from init on system shutdown. Here you should add things, that should happen directly before shuting down." Commented Sep 8, 2021 at 23:12

4 Answers 4

51

Linux Mint is based on Ubuntu, so I'm guesing the runlevel system is probably the same. On Ubuntu, scripts for the different runlevels are executed according to their presence in the /etc/rc[0-6].d directories. Runlevel 0 corresponds to shutdown, and 6 to reboot.

Typically the script itself is stored in /etc/init.d, and then symlinks are placed in the directories corresponding to the runlevels you require.

So in your case, write your script, store it in /etc/init.d/, then create a symlink in each of /etc/rc0.d and /etc/rc6.d (if you want both) pointing to your script.

The scripts in each runlevel directory will be executed in asciibetical order, so if the order within the runlevel matters to you, choose the name of your symlink accordingly.

4
  • I just took a look at the skeleton file in init.d (kubuntu precise) and got a bit intimidated. I just want to add a couple of commands that echo the date of shutdown into a file under /var/log. Can I just do that or do I have to deal with some of the other things in the skeleton file?
    – Joe
    Commented Sep 28, 2012 at 20:02
  • 2
    @Joe - You don't need most of that stuff unless you want your script to be controllable as a standard service. You won't break anything with a simple echo to your own log file, so try it and see. Commented Sep 28, 2012 at 23:34
  • 2
    Here is a simple sample init.d script. Also, it's easier and more robust to make the symlinks using sudo update-rc.d /etc/init.d/myservice defaults
    – RolfBly
    Commented Dec 9, 2014 at 17:12
  • I have written /etc/init.d/spark_shutdown.sh and created link of in /etc/rc0.d & /etc/rc6.d now how do i confirm this script ran in ubuntu aws ec2 instance Commented Jul 26, 2017 at 8:48
19

Now that the Ubuntu variants and Mint have moved to systemd I found my old solutions based on the above to be less satisfactory. I searched around the web to find out how to do it with systemd and ended up combining others' wisdom and documenting it as a blog post at blogspot.com.au containing the following tutorial.

With systemd you create one or two files to call your scripts using the templates below, and execute a couple of commands. Simple.


GUI Version

First create the scripts you want to run at startup and/or shutdown. I created .scopening_atstart and .scfullcopy_atend.

Then make sure that they are both executable by right clicking the file, selecting properties and making sure that, under permissions, you have ticked Allow executing file as a program.

The two files I created populate and save the contents of a ramdisk. They also create a file in my home directory to prove that the service is working. They were of the form:

#!/bin/sh
cp -pru /home/john/zRamdisk/subdirectory1/* /home/john/.wine/drive_c/subdirectory1/
rm /home/john/stop_time
date +%D' '%T > /home/john/stop_time

Then I opened my file manager as root, opened /etc/systemd/system and created a file startup.service and a file save-ramdisk.service. Obviously, you can choose your own names and generic names could have included a startup file called johns_start.service and a shutdown file called johns_shutdown.service. Just don't pick existing service names.

[Unit]
Description=Startup Applications

[Service]
Type=oneshot
RemainAfterExit=false
ExecStart=/home/john/.scopening_atstart

[Install]
WantedBy=multi-user.target

and

[Unit]
Description=Save Ramdisk to Wine drive C

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/true
ExecStop=/home/john/.scfullcopy_atend

[Install]
WantedBy=multi-user.target

You can use the same service files, substituting the full path of your executable script for mine.

Finally for each one execute the command systemctl enable your_files_name (but without the suffix service). So my first one was systemctl enable startup

Reboot the computer once to start the services. The start service will be executed whenever systemd enters the multi-user target and the stop service when it exits the multi-user target. Alternative service files with different activation conditions will be described below.


CLI (Command Line) Version

This description assumes that you operate from your home directory rather than /home/john, use sudo as needed, and your choice of editor where I write vim or svim.

Create startup and shutdown shell scripts with the first line #!/bin/sh and make them executable using chmod +x my_new_filename.

Create two files as above, or in this example, one file to handle startup and shutdown tasks. I will execute scripts in my home directory but @don_crissti shows some alternatives at Stack Exchange.

vim /etc/systemd/system/start_and_stop.service

and copy in the file content:

[Unit]
Description=Run Scripts at Start and Stop

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/home/john/.startup_commands  #your paths and filenames
ExecStop=/home/john/.shutdown_commands

[Install]
WantedBy=multi-user.target

Then Enable the Service with the command:

systemctl enable start_and_stop

and reboot your system after which the services will be active. The commands systemctl is-enabled start_and_stop and systemctl is-active start_and_stop can be used in monitoring your new services.


Changing the Trigger Conditions for Shutdown

The files above all use the open or close of the multi-user environment to initiate running the scripts. The file below uses the beginning of four potential shutdown processes to initiate its scripts. Adding or removing the targets on the Before line + the WantedBy line will let you make finer distinctions:

This file was proposed in the second answer of this post but I was unable to get it to run until I added an Install section.

Again, edit the script in /etc/systemd/service/ and enable it using systemctl enable your_file_name. When I changed the targets I used the systemctl disable file_name command and then re-enabled it which symlinked it to the target directories. Reboot and the service will be operating.

[Unit]
Description=Do something required
DefaultDependencies=no
Before=shutdown.target reboot.target halt.target
# This works because it is installed in the target and will be
#   executed before the target state is entered
# Also consider kexec.target

[Service]
Type=oneshot
ExecStart=/home/john/.my_script  #your path and filename

[Install]
WantedBy=halt.target reboot.target shutdown.target
0
7

You need to use rc.shutdown the shutdown script for the Linux Kernel. Quoting the OpenBSD man page:

When the system is shut down using the reboot(8) or halt(8) commands, or when init(8) is signalled to do so, or when a keyboard-requested halt is issued (if the architecture supports it), rc(8) is invoked with the argument ``shutdown''.

So, you simply open your rc.shutdown and add whatever shell commands you want to execute to it.

UPDATE: Since Linux Mint is based on Ubuntu, which has a different startup/shutdown procedure, here is the procedure relevant to you:

Write the shell script you wish to execute and copy it to the relevant directory in your /etc/rc*.d/. The * corresponds to the runlevel at which you want the script to execute.
Ubuntu follows the Debian runlevel numbering, so you have runlevel 0 for halt and runlevel 6 for reboot.

0
1

For SysV dependency based init systems used in Debian Wheezy, Devuan and its derivates and maybe others, you can mimic the behavior of rc.shutdown analogous to rc.local like the following:

Add your commands to the following skeleton of /etc/rc.shutdown:

#!/bin/sh -e
#
# rc.shutdown
#

# <add your commands here, before exit 0>

exit 0

Save the script and make the file executable with chmod a+x /etc/rc.shutdown.

Then, add a new script /etc/init.d/rc.shutdown to your system with the following content:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          rc.shutdown
# Required-Start:
# Required-Stop:
# X-Stop-After:      umountroot
# Default-Start:
# Default-Stop:      0 6
# Short-Description: Run /etc/rc.shutdown if it exist
### END INIT INFO


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

. /lib/init/vars.sh
. /lib/lsb/init-functions

do_stop() {
    if [ -x /etc/rc.shutdown ]; then
            [ "$VERBOSE" != no ] && log_begin_msg "Running local shutdown scripts (/etc/rc.shutdown)"
        /etc/rc.shutdown
        ES=$?
        [ "$VERBOSE" != no ] && log_end_msg $ES
        return $ES
    fi
}

case "$1" in
    start)
        # No-op
        ;;
    restart|reload|force-reload)
        echo "Error: argument '$1' not supported" >&2
        exit 3
        ;;
    stop|status)
    do_stop
        exit 0
        ;;
    *)
        echo "Usage: $0 start|stop" >&2
        exit 3
        ;;
esac

Also make this script executable.

Create the symlinks in /etc/rc[06].d:

cd /etc/rc0.d ; ln -s ../init.d/rc.shutdown K01rc.shutdown

and

cd /etc/rc6.d ; ln -s ../init.d/rc.shutdown K01rc.shutdown

Use insserv to correct the symlinks and to create the necessary .depend.boot, .depend.start, and .depend.stop files:

insserv -v

You need to be root or use sudo for all commands mentioned above.

2
  • I wasted my time with this one for a bit today and could not get it to work with Ubuntu 16, perhaps it was me but the solution above worked immediately.
    – Mike Q
    Commented Mar 11, 2019 at 1:47
  • 1
    Call /usr/lib/insserv/insserv -v as last step when it cannot be found right away. Commented Jan 16, 2020 at 12:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .