0

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?

  • How much slower is normal macOS disk caching than using a RAMdisk ie is it worth the hassle? – Mark Mar 10 '18 at 11:17
  • I've been doing more testing and it does seem like the problem here is that the RAM disk is being unmounted at the same time as my script's shutdown function is running, this it can't finish copying files off ram disk before it goes away. So what I need is some way to potentially force the RAM disk to stay up for another few seconds, or perhaps to "pause" the shutdown process while this script is running. – l008com Mar 11 '18 at 10:51
  • Interestingly, in a whim I changed the line tail -f /dev/null & to tail -f /Volumes/webfolder/.handle and of course, created an invisible file called .handle on my RAM disk. The idea being that tail opening a file on the RAM disk will keep it open, and keep the ram disk un-ejectable until the script exits. It doesn't work... but it helps. I'm consistently getting MORE files copied with the cp line. But not all of them. So I may be headed in the right direction, I just need to force the RAM disk to stay open for a few more seconds. – l008com Mar 11 '18 at 15:19
1

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).

  • Mac OS X Lion came out in 2012, Tiger was 10.4, which came out in 2005. And yes, there was rc.shutdown.local back then. But it is gone now. I couldn't tell you which system was the one that got rid of it, but it's definitely not around in 10.11. As far as SIGTERM and SIGKILL, I removed the SIGKILL from my script, but it hasn't changed the results. Still gets file not found errors for /Volumes/webfolder/ I updated the script in my original post to reflect this anyway. – l008com Mar 10 '18 at 10:55
  • Regarding the launchd config, I'm not sure what you mean by having dependencies and orders configured. I've never heard of anything like that, when it comes to launchd. My launchd.plist just has the following attributes set: Label, Disabled (false), Program and RunAtLoad. Also when you think about it, how would launchd dependencies apply here, since the issue is happening when the script is quit. You also mentioned one option was to use "event trigger functionality" in launchd. Can you elaborate on that? – l008com Mar 10 '18 at 10:55
  • Lastly, I googled xpc_transaction_begin but it looks like a apple programming api, not something I can call from a shell script? As far as the RAM disk warnings, I am aware of all of that and have multiple other systems in place to keep the files safe. But yes, if anyone is reading this and thinking of doing something similar, a simple script to make the RAM disk on startup then back it up on shutdown would NOT be sufficient! – l008com Mar 10 '18 at 10:55
  • @jksoegaard-I think your writeup may be helpful for my situation, which also involves trapping the shutdown signals so a function can be run as part of bringing the system down/rebooting. Mine involves suspending a VM, and I think the OP got too much attention due to the RAM disk aspect of his question. Regardless, although it seems you implied it, can you confirm that if only the SIGTERM is trapped, a task that only takes a few seconds should be given time to finish in a shutdown? – tim.rohrer Jun 8 '18 at 4:52
  • Yes, but "a few seconds" is very system dependent. I.e. test what kind of time you're given before depending on it. – jksoegaard Jun 8 '18 at 9:21

You must log in to answer this question.

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