CSS: Providing seven essential vitamins and minerals since 1996.
Have a steaming hot, delicious slice of Standards.

A PHP Shell Script for Backups

A PHP Shell Script for Backups

August 9, 2008 by Richard

On Mac servers I love the advent of a neat feature called Time Machine.  Time Machine does incremental backups without having to deal with 3rd-party software, or custom shell scripts like the one I'm about to show.  Unfortunately, on the Linux side there's always some degree of elbow grease that's required.  Which isn't to say you couldn't have Time Machine-like backups on Linux, you certainly can, it just takes a more concentrated technical effort, and you don't get the nifty GUI.

The following is a custom PHP shell script that I wrote for doing automated backups using the rsync command.

<?php // Backup script extraordinaire! // rsync docs // http://optics.ph.unimelb.edu.au/help/rsync/rsync.html // This requires PHP CLI (Command Line Interface) // http://www.php.net/ // Just call it from the command line like this: // php -q /path/to/this/script.php (CGI version) // php /path/to/this/script.php (CLI version) // // Use the -q switch to supress the HTTP headers that PHP outputs by default. // The folder or disk to backup $source_volume = '/Volumes/Lothlorien'; // The folder or disk to backup to $destination_volume = '/Volumes/Backup'; // The number of backups $backup_count = 31; ////////////////////////////////////////////////////////////////////////////////////// // Don't edit below this line! ////////////////////////////////////////////////////////////////////////////////////// // Make sure the max is unlimited. ini_set('max_execution_time', 0); // If the destination and the source exist if (file_exists($destination_volume)) { if (is_writable($destination_volume)) { if (file_exists($source_volume)) { // Get the contents of the cursor file if (file_exists($destination_volume.'/cursor.txt')) { $cursor = @file_get_contents($destination_volume.'/cursor.txt'); } else { $cursor = null; } if (!empty($cursor)) { $cursor = explode('|', $cursor); $counter = (int) $cursor[0]; // The counter is before the vertical bar $date = (int) $cursor[1]; // The date is after the vertical bar } else { // There is no cursor yet $counter = 1; $date = 0; } // If the day in the cursor and the day now don't match, // go ahead and do a backup. This ensures that only one // daily backup is done. if (date('j', $date) != date('j') || empty($date)) { // Open the cursor file for writing $f_cursor = fopen($destination_volume.'/cursor.txt', 'w+'); // Make the Backups directory, if it doesn't exist if (!file_exists($destination_volume.'/Backups')) { mkdir($destination_volume.'/Backups', 0755); } // Set up individual backup directories on the first run for ($i = 1; $i <= $backup_count; $i++) { if (!file_exists($destination_volume.'/Backups/'.$i)) { mkdir($destination_volume.'/Backups/'.$i, 0755); } } // Output which backup is being written to the terminal echo "Active backup: {$destination_volume}/Backups/{$counter}\n"; // Run the rsync command, and sync up the backup. `rsync -r {$source_volume} {$destination_volume}/Backups/{$counter} --delete --ignore-errors`; // Increment the counter for tomorrow's backup. $counter++; // Reset the counter back to one if the counter has // exceded the backup count. if ($counter > $backup_count) { $counter = 1; } // Write the cursor to a file on the backup volume, so it can be // referenced for next time, and so you can determine which // backup is the most up to date fwrite($f_cursor, $counter.'|'.mktime()); fclose($f_cursor); } else { // Otherwise, it's still today's backup, and today's backup has already ran. // Since today's backup has already ran, sync today's backup again // Go back one, so you're not syncing to tomorrow's backup if ($counter > 1) { $counter--; } else { $counter = $backup_count; } // Ouput which backup is being written to the terminal echo "Active backup: {$destination_volume}/Backups/{$counter}\n"; // Run the rsync command, and sync up the backup `rsync -r {$source_volume} {$destination_volume}/Backups/{$counter} --delete --ignore-errors`; } } else { echo "Error: The source volume {$source_volume} does not exist or is not mounted.\n"; } } else { echo "Error: The destination volume {$destination_volume} is not writable.\n"; } } else { echo "Error: The destination volume {$destination_volume} does not exist or is not mounted.\n"; } ?>

The backup script makes 31 uncompressed backups of the same content.  I prefer uncompressed copies because that makes restoring from backup much easier.  The script could easily be modified to archive and compress using the switches offered by rsync.  And the script could also be modified to make more or less than 31 backups, I find a month a good round number for backups, combine this script with multiple external hard drives, and you can go back in time as much as you like.

This script backups to the same copy for 24 hours, after 24 hours, it will go on to the next, until it gets to 31, then it will start over at 1 again.  I typically set this script to run on a cronjob on Linux or, on a Mac, you can link to it from an AppleScript, then set the Apple Script up as an event in iCal.  Alternatively you can automate it via launchd as well if the iCal approach isn't feasible.

Using rsync for the backup processes makes the backup more efficient with time, since it only syncs changes since the last time a backup was done.

A crafty onlooker might see a lot of opportunity for adding command line arguments to this script that would let you passthrough the configurations automatically from the shell...  I could have done that too, but the script suited my needs as it is written, so I didn't bother.

Et voilà! A PHP shell script for backups.  Help yourself.

Comments

  • December 16, 2009 John says:

    I'd like to start some open source plugin projects, and have other developers contribute.
    I could use SourceForge or some other host, but it would be really nice to keep it inside WordPress.org.
    If you want to see my plugin visit torrent search engine http://www.picktorrent.com

Leave a Comment

Archive

PHP Powered XHTML 1.0 CSS
Copyright © 1998-2010 Richard York, All Rights Reserved.