#!/bin/bash # ************************************* # # # # sshbackup # # # # ************************************* # # **** 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 " -b, --bandwidth bandwidth limit in kbit/s" echo echo " -d, --deploy deploy settings to remote host" echo " -n, --no-root run without root privileges" echo } version() { echo echo -e "vesion: \033[1;37m$version\033[0m" echo "author: $author" echo } pipewrap() { echo $1 local lockfile=$2; while true; do if [ ! -e $lockfile ]; then return 0 fi read -t 1 line if [ $? -eq 0 ]; then echo $line fi done } sshsudo() { local machine=$1 local password="" local script=$2 local arguments=$3 read -s -p "please enter your [sudo] password for $machine:" password echo local remotescript=/tmp/sshsudo_`date +%s` sshpass -p "$password" scp -q "$script" "$machine:'$remotescript'" sshpass -p "$password" ssh -q "machine" "sudo -K" local lockfile=`mktemp` eval pipewrap '$password' '$lockfile' | (sshpass -p "$password" ssh -q "$machine" "sudo -S '$remotescript' $arguments"; rm "$lockfile") sshpass -p $password ssh -q "$machine" rm $remotescript } interactive() { tty -s return $? } amiroot() { if [ $(id -u) -eq 0 ]; then return 0 else return 1 fi } findhome() { #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 #return home directory echo $home } preflight() { #amiroot? if ( ! amiroot ) && [ $noroot -eq 0 ]; then echo "aborting mission. you need be root or use the --no-root option." 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 #find the executing users home directory local home=$(findhome) #if $sshkeyfile is not set use this path 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 #bandwidth limit if [ $limit -gt 0 ]; then limit=$((limit/8)) cmdopt="$cmdopt --bwlimit=$limit" fi #run rsync $localcmd $cmdopt -e "ssh -q -i $sshkeyfile" --rsync-path="$remotecmd" $sourcepath $destpath/0 if [ $? -ne "0" ]; then echo "an error occured while running backup for $sourcepath" 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 } # **** 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 bandwidth=0 limit=0 list=0 noroot=0 deploy=0 options=$* # **** 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 ;; -b|--bandwidth) bandwidth=1 ;; -n|--no-root) noroot=1 ;; -d|--deploy) deploy=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 [ $bandwidth -eq 1 ]; then if [ -z "${option//[0-9]/}" ]; then limit=$option else echo "aborting mission. unknown bandwidth limit given. [$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 elif [ $deploy -eq 1 ]; then #TODO: do nothing for now : deploy=0 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 ****