#!/usr/bin/perl -w ######################################################################### # # # Copy the home directory to/from elsewhere # # Copyright (C) 2002-11, John Zaitseff # # # ######################################################################### # Author: John Zaitseff # Date: 7th May, 2011 # Version: 1.37 # This script copies the home directory (in $HOME) to and from another # location, usually removable storage or a remote system. It uses the # RSYNC program over SSH (Secure Shell) to do this efficiently and # securely. # # Syntax: # cphome -t [-d] [-n] dest - to copy TO the destination "dest" # cphome -b [-d] [-n] src - to copy BACK from the source "src" # # where "-n" performs a dry run (does not actually do the operation) and # "-d" forces extraneous files in the destination directory to be deleted. # Both "dest" and "src" must either be a local fully-qualified path (ie, # starting with "/") or a remote hostname (optionally ending in a colon) # or both (separated by a colon). Options may be combined in the standard # UNIX fashion. # # Please note that this script specifically does NOT copy (nor even # examine) certain files and directories in the home directory. These # include backup files and known temporary file locations (such as for Web # browser caches). You can specify which files and directories are # excluded by modifying this script: see the $rsync_excl variable below. # # # Examples (assuming $HOME is set to "/home/john"): # # cphome -b /zip # Copy all files from the source directory "/zip/home/john" to the # home directory "/home/john". Any files in the home directory that # are not in the source directory are ignored. Newer files in the # destination directory are NOT overwritten. # # cphome -b -d /zip # Same as the above example, except that any files in the destination # directory "/home/john" that are not in "/zip/home/john" ARE deleted. # # cphome -t zap.org.au # Copy all files from the home directory "/home/john" to the # destination directory "/home/john" on the host "zap.org.au". Any # files in "/home/john" at "zap.org.au" that are not present in the # (local) source directory are ignored. Newer files in the # destination directory are not overwritten. # # cphome -nt zap.org.au # Same as the above, but don't actually perform the operation: just # list what would be done. Note that the "-n" and "-t" options are # combined. # # cphome -d -b altair.zap.org.au:/zip # Copy all files from the "/zip/home/john" directory on the host # "altair.zap.org.au" to the (local) home directory "/home/john". Any # files in the (local) destination directory that are not present on # the remote host ARE deleted. Newer files are not overwritten. # This program is free software. You may distribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either Version 2 of the license, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ######################################################################### # Configuration parameters and default values use strict; # Enforce better programming habits (our $O = $0) =~ s,^.*/,,; # Script name (without path) our $version = "1.37"; # Script version # Programs and options needed for this script to function our $ssh_prog = "ssh"; # SSH binary our $rsync_prog = "rsync"; # RSYNC binary our $rsync_opts = "-vauzS"; # Default options to RSYNC our $rsync_dry = "-n"; # Dry-run options our $rsync_del = "--delete"; # Delete options our $rsync_excl = # Files/directories to exclude "--exclude /.DCOPserver* " . "--exclude /.FontForge/autosave/ " . "--exclude /.MCOP-random-seed " . "--exclude /.ICEauthority " . "--exclude /.Xauthority " . "--exclude /.adobe/Flash_Player/AssetCache/ " . "--exclude /.bash_history " . "--exclude /.cache/ " . "--exclude /.dbus/session-bus/ " . "--exclude /.dvdcss/ " . "--exclude /.emacs.d/auto-save-list/ " . "--exclude /.fontconfig/*.cache* " . "--exclude /.fonts.cache* " . "--exclude /.gegl-*/swap/ " . "--exclude /.gimp-*/tmp/ " . "--exclude /.gstreamer-*/registry*.bin " . "--exclude /.kde*/Trash " . "--exclude /.kde*/share/apps/RecentDocuments/ " . "--exclude /.kde*/share/apps/amarok/albumcovers/cache/ " . "--exclude /.kde*/share/apps/gwenview/recentfolders/ " . "--exclude /.kde*/share/apps/gwenview/recenturls/ " . "--exclude /.kde*/share/apps/k3b/lastlog.log " . "--exclude /.kde*/share/apps/k3b/temp/ " . "--exclude /.kde*/share/apps/kconf_update/log/ " . "--exclude /.kde*/share/apps/klipper/history*.lst " . "--exclude /.kde*/share/apps/kpdf/[0-9]*.pdf.xml " . "--exclude /.kde*/share/apps/nepomuk/ " . "--exclude /.kde*/share/apps/okular/docdata/ " . "--exclude /.kde*/cache-* " . "--exclude /.kde*/socket-* " . "--exclude /.kde*/tmp-* " . "--exclude /.lesshst " . "--exclude /.libreoffice/3/user/backup/ " . "--exclude /.libreoffice/3/user/temp/ " . "--exclude /.lilypond-fonts.cache*/ " . "--exclude /.local/share/Trash/ " . "--exclude /.local/share/akonadi/ " . "--exclude /.local/share/marble/maps/ " . "--exclude /.mozilla/firefox/Crash* " . "--exclude /.mozilla/firefox/*/Cache/ " . "--exclude /.mozilla/firefox/*/OfflineCache/ " . "--exclude /.mozilla/firefox/*/bookmarkbackups/ " . "--exclude /.mozilla/firefox/*/urlclassifier?.sqlite " . "--exclude /.ncftp/trace* " . "--exclude /.openoffice.org/3/user/config/imagecache/ " . "--exclude /.openoffice.org/3/user/temp/ " . "--exclude /.openoffice.org2/user/config/imagecache/ " . "--exclude /.openoffice.org2/user/temp/ " . "--exclude /.opera/cache* " . "--exclude /.opera/images/ " . "--exclude /.opera/opcache* " . "--exclude /.opera/temporary_downloads/ " . "--exclude /.opera/thumbnails/ " . "--exclude /.opera/vps/ " . "--exclude /.pulse* " . "--exclude /.recently-used " . "--exclude /.sudo_as_admin_successful " . "--exclude /.thumbnails/ " . "--exclude /.xsession-errors* " . "--exclude /lib/mail/*_[0-9]* " . "--exclude '*~' --exclude '*.BAK' --exclude '*.bak' " . "--exclude '#*' --exclude '.#*'"; # Environment variables our $home = $ENV{HOME} || (getpwuid($>))[7]; $ENV{PATH} = "/bin:/usr/bin:/usr/local/bin"; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Function prototypes sub showusage(); sub showversion(); sub showcmdlerr(@); sub findprog($); sub rsync($); ######################################################################### # Initialise global variables our $opt_to = 0; # Copying to dest? our $opt_back = 0; # Copying back from src? our $opt_del = 0; # Delete extraneous files? our $opt_dry = 0; # Perform a dry run? our $target; # Target directory our $isremote; # Is the target a remote one? our $host; # Host portion of target our $dir; # Directory portion of target ######################################################################### # Main program die "$O: Cannot access $home\n" if (! -d $home || ! -r $home); findprog($rsync_prog); findprog($ssh_prog); $rsync_opts .= " -e $ssh_prog"; # Use SSH with RSYNC # Process command line arguments while ($_ = $ARGV[0]) { last if (! /^-/); shift @ARGV; last if ($_ eq '--'); # Split combined short-form options into single arguments if (/^-(\w{2,})/) { my @args = split //, $1; foreach my $arg (@args) { $arg = "-$arg"; } unshift @ARGV, @args; next; } # Process command-line options if (($_ eq "--to") || ($_ eq "-t")) { $opt_to = 1; } elsif (($_ eq "--from") || ($_ eq "--back") || ($_ eq "-b")) { $opt_back = 1; } elsif (($_ eq "--dry-run") || ($_ eq "-n")) { $opt_dry = 1; } elsif (($_ eq "--delete") || ($_ eq "-d")) { $opt_del = 1; } elsif (($_ eq "--help") || ($_ eq "-h") || ($_ eq "-?")) { showusage(); } elsif (($_ eq "--version") || ($_ eq "-V")) { showversion(); } else { showcmdlerr("Unrecognised option: $_"); } } showcmdlerr("Missing parameter") if $#ARGV < 0; showcmdlerr("Too many parameters") if $#ARGV > 0; # Check for option sanity die "$O: Cannot have both -t and -b\n" if $opt_to && $opt_back; die "$O: Must specify either -t or -b\n" if ! $opt_to && ! $opt_back; # Set various RSYNC options $rsync_opts .= " $rsync_dry" if $opt_dry; $rsync_opts .= " $rsync_del" if $opt_del; # Set target directory and associated variables $target = $ARGV[0]; if ($target =~ /:/) { # Target (source or destination) has a colon in it $isremote = 1; $target =~ /^(.*?):(.*)$/; $host = $1; $dir = $2; die "$O: Missing remote hostname parameter\n" if ($host eq ""); } elsif ($target =~ /\//) { # Target has a slash in it $isremote = 0; $host = undef; $dir = $target; die "$O: Missing directory parameter\n" if ($dir eq ""); } else { # Target has neither a slash or a colon: treat as a hostname $isremote = 1; $host = $target; $dir = ""; die "$O: Empty parameter\n" if ($host eq ""); } if ($dir =~ /^(.*?)\/+$/) { $dir = $1; } $dir = $dir . $home; if ($isremote) { $dir = $host . ":" . $dir; } die "$O: Cannot access $dir: $!\n" if (! $isremote && ! -r $dir); # Perform the actual operation if ($opt_to) { # Copy files from $home to $dir rsync("$rsync_prog $rsync_opts $rsync_excl $home/ $dir/"); } elsif ($opt_back) { # Copy files back from $dir to $home rsync("$rsync_prog $rsync_opts $rsync_excl $dir/ $home/"); } ######################################################################### # Display help information and terminate sub showusage () { print <<"DATAEND"; $O v$version: Copy the home directory to/from elsewhere. Copyright (C) 2002-11, John Zaitseff. This script copies the home directory (in \$HOME) to and from another location, usually removable storage or a remote system. It uses the RSYNC program over SSH (Secure Shell) to do this efficiently and securely. Syntax: $O -t [-d] [-n] dest - to copy TO the destination \"dest\" $O -b [-d] [-n] src - to copy BACK from the source \"src\" where "-n" performs a dry run (does not actually do the operation) and "-d" forces extraneous files in the destination directory to be deleted. Both "dest" and "src" may either be a local fully-qualified path (ie, starting with "/") or a remote hostname (optionally ending in a colon) or both (seperated by a colon). Options may be combined in the standard UNIX fashion. For more information, please read the source code of the script. This may be found in $0. DATAEND ; exit(0); } ######################################################################### # Display program version information and terminate sub showversion() { print <<"DATAEND" $O v$version: Copy the home directory to/from elsewhere. Copyright (C) 2002-11, John Zaitseff. This program, including associated files, is distributed under the GNU General Public License. See the file COPYING for more information. DATAEND ; exit(0); } ######################################################################### # Show an error message relating to the command-line and terminate sub showcmdlerr(@) { map { warn "$O: $_\n" } @_; die "\nUsage:\n" . " $O -t [-d] [-n] dest - to copy TO the destination \"dest\"\n" . " $O -b [-d] [-n] src - to copy BACK from the source \"src\"\n" . " $O --help - to display more help\n"; } ######################################################################### # Find location of programs sub findprog ($) { my $prog = $_[0]; my @path = split(/:/,$ENV{PATH}); foreach my $pp (@path) { if ( -x $pp . "/" . $prog ) { $_[0] = $pp . "/" . $prog; # Update parameter to include path return; } } die "$O: $prog: Could not find executable in $ENV{PATH}\n"; } ######################################################################### # Perform RSYNC operation sub rsync ($) { my $cmdline = $_[0]; open(RSYNC, "$cmdline |") || die "$O: Cannot execute rsync: $!\n"; # Filter unwanted output while () { chomp; if (! /^building file list ... done$/ && ! /^receiving file list ... done$/ && ! /^wrote \d+ bytes\s+read \d+ bytes\s+.+sec$/ && ! /^sent \d+ bytes\s+received \d+ bytes\s+.+sec$/ && ! /^total size is \d+\s+speedup is .+$/ && ! /^rsync: open connection using / && ! /^rsync: building file list...$/ && ! /^rsync: \d+ files to consider.$/ && ! /^receiving incremental file list$/ && ! /^sending incremental file list$/ && ! /^\s*$/) { print "$_\n"; } } close(RSYNC); }