Shutdown Script in macOS / RAM Disk Backup

shutdown

I'm attempting to create a shutdown script in macOS (10.11) – that is, a script that runs at shutdown, not a script that shut's down the Mac. The linux "rc" system is not present in macOS.

I've searched and searched for a solution, and this is the only one I've been able to find. It launches at boot with launchd, and both the startup and shutdown functions run when they should:

#!/bin/bash

function startup()
    {
    ## commands to create and fill ram disk

    tail -f /dev/null &
    wait $!
    }

function shutdown()
    {
    ## commands to backup contents of ramdisk

    exit 0
    }

trap shutdown SIGTERM

startup;

As I said, both the startup() and the shutdown() functions run when expected. The problem lies within the commands of the shutdown function. It's a pretty simple script, it just copies the contents of the ram disk to a folder on the hard drive:

function backup_ramdisk()
    {
    ## empty ram disk backup folder
    rm -R -f /webfolder-backup/*

    ## copy contents of the ramdisk to the ramdisk backup
    cp -R /Volumes/webfolder/ /webfolder-backup/

    ## make me the owner
    chown -R me /webfolder-backup/
    chmod -R 777 /webfolder-backup/ 

    exit 0
    }

The actual script is loaded with extras that log stuff, and I can confirm that the entire script does run. But what happens is the cp line fails. Sometimes it fails completely, and the backup folder is empty. But more often, it gets part of the structure of the RAM disk, and stops copying files right around (but not exactly) the same spot.

Often times it will throw the error cp: /Volumes/webfolder/: No such file or directory even though it has already copied a hundred items from that folder. It makes me think that the shutdown command is unmounting the RAM disk before my script has time to finish backing it up. Keep in mind, because it's a RAM disk, it only takes a couple of seconds to copy all the files off of it. But it seems that's not enough. If there were a way I could pause the shutdown process while my script is running, then proceed, that would be ideal! Or maybe a better approach to this entirely?

Best Answer

Regarding your comparison with Linux: Mac OS X did indeed have the "rc" system, you know from Linux (it is really called a SysV init system). Back in 2005 this system was abandoned with OS X Lion, and the more modern launchd system was introduced instead - leading to, amongst other things, much faster boots.

On Linux the same change has happened, however later than on macOS. Nowadays Linux distributions commonly use systemd, upstart or similar "modern" take on an init system.

Regarding your solution - it is really a hack, and it not done the right way. This is why you're having issues. You're trapping both SIGTERM and SIGKILL - this is against recommendations, and it is the cause of the "No such file or directory" error message.

Basically what happens on shutdown (this is completely similar to Linux) is that each process is sent SIGTERM to let it know that it should terminate. The process then only has a very short time to close (i.e. within few seconds) - otherwise the system sends the SIGKILL signal to really kill off the process if it is not responding.

In your case when the copying takes a bit too long, you'll trap both signals and you will run your shutdown() function twice.

You haven't included your launchd configuration (i.e. probably a daemon plist), but you need to be careful to have dependencies and orders configured here in order for your hack to work. Otherwise you risk the RAM disk being unmounted before shutdown is called (which is probably what happens when you run shutdown the second time due to the SIGKILL signal.

As far as I remember the /etc/rc.shutdown.local file is still a valid method to run programs at shutdown. This is by far the easiest way of doing it.

Simply create the /etc/rc.shutdown.local file and make it executable. It should start with a shebang like your current script, but only needs to contain the commands you use to copy over the files to your backup.

If you want to do it properly with launchd you could either modify your hack to handle signals properly, and then use the xpc_transaction_begin/end functions to let the system know that your program is doing useful work, and shutdown should be delayed until finished -- or alternatively use the event trigger functionality in launchd.

I would recommend you to try /etc/rc.shutdown.local first. It is deprecated, but as far as I remember still working.

A small warning is probably also a good idea for you. Remember that you are never guaranteed that your shutdown program will run. It is therefore not a good idea to keep stuff you don't want to loose in a RAM-disk like that. A single power outage and your data is lost. Instead use frequent data synchronisations or similar to ensure that data is periodically backed up to permanent storage.

Regarding your request whether a better approach exists - then the answer is "most likely, yes". The idea of copying a web folder to RAM disks seems a bit historic and dated. We have not information about what you're doing with that web folder, but if you're just serving web sites and want it to be fast by using a RAM-disk - that is the wrong approach. It is much better to use the built-in vfs caching functionality of macOS (similar to any other modern operating system, such as Linux).

Related Question