Cron and rsync using file lock but is there a better way on Mac OS X Server?
I have this bash script and is run via cron on a regular interval:
#!/bin/bash
RSYNC=/usr/bin/rsync
SSH=/usr/bin/ssh
KEY=/Users/admin/Documents/Backup/rsync-key
RUSER=philosophy
RHOST=example.com
RPATH=data/
LPATH="/Volumes/G Technology G Speed eS/Backup"
LOCKFILE=/Users/admin/Documents/backup.isrunning
if [ ! -e $LOCKFILE ]; then
touch $LOCKFILE
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
rm $LOCKFILE
else
echo "Rsync - Backup still running"
fi
The backup could take any length of time, from a few minutes to days and if I run the backup via cron every 6 hours what I dont want is two instances of this running at the same time. So what I've done is created a simple locking mechanism. But I'm woried that if the script is killed half way through for what ever reason that lock file is always going to be there and the backup routine is not going to run.
Is there a way of enhancing this to be better fool proof?
Thanks
Scott
EDIT: Final bash script I'm now using thanks to the answer below:
#!/bin/bash
RSYNC=/usr/bin/rsync
SSH=/usr/bin/ssh
KEY=/Users/admin/Documents/Backup/rsync-key
RUSER=philosophy
RHOST=example.com
RPATH=data/
LOCKFILE=/Users/admin/Documents/Backup/backup.isrunning
if [ ! -e $LOCKFILE ]
then
echo $$ >"$LOCKFILE"
else
PID=$(cat "$LOCKFILE")
if kill -0 "$PID" >&/dev/null
then
echo "Rsync - Backup still running"
exit 0
else
echo $$ >"$LOCKFILE"
echo "Warning: previous backup appears to have not finished correctly"
fi
fi
LPATH="/Volumes/G Technology G Speed eS/Backup"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
LPATH="/Volumes/G Technology G Speed eS/Catalogue"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
LPATH="/Volumes/G Technology G Speed eS/Digital"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
LPATH="/Volumes/G Technology G Speed eS/Finance"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
LPATH="/Volumes/G Technology G Speed eS/Image Libraries"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
LPATH="/Volumes/G Technology G Speed eS/IT Desk"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
LPATH="/Volumes/G Technology G Speed eS/Office"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
LPATH="/Volumes/G Technology G Speed eS/Studio"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
LPATH="/Volumes/G Technology G Speed eS/Toffee Apple"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
rm -f "$LOCKFILE"
Solution 1:
If you put the PID of the script into the lock file then, if the lock file exists when a new instance starts, you can read the PID and check if the script is still running. If the script isn't running then clean up, put the PID of the current instance in the lock file and start the rsync otherwise print a 'still running' message and exit.
EDIT:
#!/bin/bash
backup() {
RSYNC=/usr/bin/rsync
SSH=/usr/bin/ssh
KEY=/Users/admin/Documents/Backup/rsync-key
RUSER=philosophy
RHOST=example.com
RPATH=data/
LPATH="/Volumes/G Technology G Speed eS/Backup"
$RSYNC -avz --delete --progress -e "$SSH -i $KEY" "$LPATH" $RUSER@$RHOST:$RPATH
}
LOCKFILE=/Users/admin/Documents/backup.isrunning
if [ ! -e "$LOCKFILE" ]
then
echo $$ >"$LOCKFILE"
backup
else
PID=$(cat "$LOCKFILE")
if kill -0 "$PID" >&/dev/null
then
echo "Rsync - Backup still running"
exit 0
else
echo $$ >"$LOCKFILE"
echo "Warning: previous backup appears not to have finished correctly"
backup
fi
fi
rm -f "$LOCKFILE"
Solution 2:
If you are configuring multiple backups then you may wish to take a look at LBackup which offers locking and also fully supports pull backups. More information regarding the advantages of a pull backup is available from the following URL : http://www.lbackup.org/network_backup_strategies
If you check the LBackup source code you will see how the lock system is implemented using a trap. The suggestion above regarding using a PID is also a good idea. With that approach, should the entire server crash and the trap not have a chance to be activated.