#!/bin/bash # ************************************* # # # # sshbackup # # # # ************************************* # # **** config section **** version="0.4.0" author="david@socialnerds.org" configfile="$HOME/.sshbackup" #rsync options. rsyncoptions="-qpogEthrzl --numeric-ids --no-motd" #dotglob option removes bug while rsyncing folder with no visible files in it. remotecmd="shopt -s dotglob; /usr/bin/sudo /usr/bin/rsync" localcmd="/usr/bin/rsync" versions=999 config=0 sshkey=0 list=0 options=$* # **** function definitions **** bashtrap() { echo echo "CTRL+C detected." echo "commiting suicide." exit 1 } usage() { echo echo "usage: sshbackup source destination [versions]" echo "source/destination example: [[user@]server:]/path/to/files" echo echo "OPTIONS:" echo " -h, --help show this message" echo " -v, --version version information" echo echo " -l, --list list of sources and destinations" echo " -c, --config alternate config file [~/.sshbackup]" echo " -s, --sshkey alternate sshkey [~/.ssh/id_rsa]" echo } version() { echo echo -e "vesion: \033[1;37m$version\033[0m" echo "author: $author" echo } interactive() { tty -s return $? } amiroot() { if [ $(id -u) -eq 0 ]; then return 0 else return 1 fi } preflight() { #amiroot? if ( ! amiroot ); then echo "aborting mission. you must be root" return 1 fi #source and destination path? if [ -z $sourcepath ]; then echo "aborting mission. no source path given." return 1 elif [ -z $destpath ]; then echo "aborting mission. no destination path given." return 1 fi #if there is a remote source or destination check for ssh key if [[ $sourcepath =~ .*@.* ]] || [[ $destpath =~ .*@.* ]]; then #get executing user id local userid=$(id -u) #find out where executing users $HOME is while read line; do line=$(echo $line | sed 's/ //g') line=$(echo $line | sed 's/:/ /g') if [ $(echo $line | awk '{print $3}') -eq $userid ]; then local home=$(echo $line | awk '{print $6}') fi done < /etc/passwd if [ -z $sshkeyfile ]; then sshkeyfile="$home/.ssh/id_rsa" fi if [ -r $sshkeyfile ]; then #ssh key found : else if ( interactive ); then echo "no ssh key found" echo -e "do you want to create a ssh key pair? [y/n] \c" read choice if [ -z $choice ]; then echo "aborting mission. no ssh key found." return 1 elif [ $choice == "y" ] || [ $choice == "Y" ]; then #creating ssh key pair ssh-keygen #TODO: key should also be deployed to remote side return 1 #for now i'll break up here fi else echo "aborting mission. no ssh key found." return 1 fi fi fi return 0 } sshbackup() { #creating local rsync options var local cmdopt="$rsyncoptions" #move existing versions local num=$versions while [ -d "$destpath/0" ]; do if [ -d "$destpath/$num" ]; then mv $destpath/$num $destpath/$((num+1)) fi let num-- done #create destpath if not existing mkdir -p $destpath/0 #add link-destination option if existing if [ -d $destpath/1 ]; then cmdopt="$cmdopt --link-dest=$destpath/1" fi #run rsync $localcmd $cmdopt -e "ssh -q -i $sshkeyfile" --rsync-path="$remotecmd" $sourcepath $destpath/0 if [ $? -ne "0" ]; then return 1 fi #removing obsolet version(s) local i=1 while [ -d $destpath/$((versions+i)) ]; do rm -rf $destpath/$((versions+i)) let i++ done return 0 } # **** start of script **** #initialize bashtrap trap bashtrap INT # **** option handler **** #end script if no options are given if [ -z $1 ]; then usage exit 0 fi for option in $options; do case "$option" in -h|--help) usage exit 0 ;; -v|--version) version exit 0 ;; -c|--config) config=1 ;; -l|--list) list=1 ;; -s|--sshkey) sshkey=1 ;; *) if [ $config -eq 1 ]; then if [ -r "$option" ]; then configfile=$option config=0 else echo "aborting mission. cannot read configfile. [$option]" exit 1 fi elif [ $sshkey -eq 1 ]; then if [ -r $option ]; then sshkeyfile=$option sshkey=0 else echo "aborting mission. cannot read sshkeyfile. [$option]" exit 1 fi elif [ $list -eq 1 ]; then if [ -r $option ]; then listfile=$option list=0 else echo "aborting mission. cannot read listfile. [$option]" exit 1 fi else if [[ $option =~ ^-.* ]]; then echo "aborting mission. unknown option given. [$option]" usage exit 1 #TODO: what if source or destination is a number? elif [ -z "${option//[0-9]/}" ]; then versions="$option" else if [ -z "$sourcepath" ]; then if [[ $option =~ .*@.* ]]; then sourcepath="${option%/}/*" elif [[ $option =~ ^/.* ]]; then sourcepath="${option%/}/*" else sourcepath="$(pwd)/${option%/}/*" fi else if [ -z "$destpath" ]; then if [[ $option =~ ^/.* ]]; then destpath="${option%/}" else destpath="$(pwd)/${option%/}" fi fi fi fi fi ;; esac done #read config if there is one if [ -r "$configfile" ]; then source "$configfile" fi #read list if listfile is given if [ -r "$listfile" ]; then while read line; do #find first letter fletter=${line:0:1} if [ -z $fletter ]; then #skip line it's empty : elif [ $fletter = "#" ]; then #skip line it's a comment : else sourcepath="" destpath="" for option in $line; do if [ -z "${option//[0-9]/}" ]; then versions="$option" else if [ -z "$sourcepath" ]; then if [[ $option =~ .*@.* ]]; then sourcepath="${option%/}/*" elif [[ $option =~ ^/.* ]]; then sourcepath="${option%/}/*" else sourcepath="$(pwd)/${option%/}/*" fi else if [ -z "$destpath" ]; then if [[ $option =~ ^/.* ]]; then destpath="${option%/}" else destpath="$(pwd)/${option%/}" fi fi fi fi done if ( preflight ); then sshbackup fi fi done < "$listfile" else if ( preflight ); then sshbackup fi fi exit 0 # **** end of script ****