Jul. 27th, 2012

mbarrick: (Default)

The problem: creating "TimeMachine-like" snapshots of changed files to a NAS device that is not TimeMachine-compatible.

Caveats with this solution: file permissions will not be maintained in the backup copies.

Overview: uses find and rsync in a shell script called with a launchd daemon.

1. The script

The script uses a combination of find and rsych to find files altered in the last four hours and to copy them to a NAS device with the directory structure intact. One very slight drawback is the whole path is recreated including /Volumes/[drivename]/...etc.

#!/bin/bash
#create a folder to mount the NAS share to. If the folder already exists, this will do nothing 
mkdir /Volumes/Backup 

#mount the NAS share to the above folder. Replace NASxxx as appropriate for your configuration 
mount -t smbfs //NASuser:NASpass@NASaddress/NASsharename /Volumes/Backup 

#this loop will maintain 20 snapshots numbered 0 to 19, suitable for four daily snapshots, five days a week 
if [ -d /Volumes/Backup/snapshot.19 ] ; then 
  rm -rf /Volumes/Backup/snapshot.19 
fi 
for i in {18..0} do 
  j=$[i+1] 
  if [ -d /Volumes/Backup/snapshot.$j ] ; then 
   rm -rf /Volumes/Backup/snapshot.$j 
  fi 
  mv /Volumes/Backup/snapshot.$i /Volumes/Backup/snapshot.$j 
done 

#using find to scour various paths for files changed within the last four hours and rsync 
#them to the mounted NAS 
find /Volumes/DRIVE1/PATH1/ -mmin -360 -type f -exec rsync -aR {} /Volumes/Backup/snapshot.0/ \; 
find /Volumes/DRIVE1/PATH2/ -mmin -360 -type f -exec rsync -aR {} /Volumes/Backup/snapshot.0/ \; 
find /Volumes/DRIVE2/PATH1/ -mmin -360 -type f -exec rsync -aR {} /Volumes/Backup/snapshot.0/ \; 

2. The launchd plist file

I will not be covering how exactly to create a launchd plist file, there is plenty of documentation for creating launch daemons and agents. What I found lacking, however was how to create a launchd plist to run a script at specific intervals. I found plenty of stuff on running at regular intervals, say every X minutes, but nothing good on how to launch at specific times, in this case 8 a.m., 12 p.m., 4 p.m. and 8 p.m. every Monday through Friday, i.e. the launchd plist equivalent to a cron task as such:

0 8,12,16,20 * * 1-5 /usr/local/bin/snapshot.sh

So here is the equivalent launchd plist (and, as a sarcastic aside--Gee, thanks, Apple. This is so much simpler and straightforward.) This is set to run using the "administrator" account. It is the <array> of <dict> that defines the individual run times.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>AbandonProcessGroup</key>
    <true/>
    <key>Debug</key>
    <true/>
    <key>Label</key>
    <string>local.snapshot</string>
    <key>Program</key>
    <string>/usr/local/bin/snapshot.sh</string>
    <key>RunAtLoad</key>
    <false/>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Hour</key>
            <integer>8</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>1</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>12</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>1</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>16</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>1</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>20</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>1</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>8</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>2</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>12</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>2</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>16</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>2</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>20</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>2</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>8</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>3</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>12</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>3</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>16</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>3</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>20</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>3</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>8</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>4</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>12</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>4</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>16</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>4</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>20</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>4</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>8</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>5</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>12</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>5</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>16</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>5</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>20</integer>
            <key>Minute</key>
            <integer>0</integer>
            <key>Weekday</key>
            <integer>5</integer>
        </dict>
    </array>
    <key>UserName</key>
    <string>administrator</string>
</dict>
</plist>
Source: http://www.mbarrick.net/blog/120727/automated-snapshots-mac-os-x-lion-server

January 2026

S M T W T F S
    123
45 67 8910
11 121314 15 16 17
18 19 20 21 22 2324
25262728293031

Expand Cut Tags

No cut tags
Page generated Jan. 26th, 2026 09:18 am
Powered by Dreamwidth Studios