#!/bin/sh # Safering updating, so - copying current daily backup directory from remote # server to local. Used SSH2 publickey auth method, so you need a working # installation before starting # # Required next software installed: # # - SSH2 from SSH Communications Inc. or OpenSSH # - BZip2 # # Required these variables in /etc/periodic.conf # # daily_backup_owner="owner" # daily_backup_group="group" # daily_backup_mode="octal_mode_like_0644" # daily_backup_dirmode="octal_mode_like_0755" # # Written by CityCat 23.07.2004 # BSD License. Copyright (C) by Rashid N. "CityCat" Achilov # $Id: safecopy,v 1.1.1.1 2008/01/20 21:10:58 shelton Exp $ PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin # Usage printing function # Input: revisionNumber (string) - Program revision number, taken from CVS # Output: none usage() { cat << !EOM Safecopy, a safe remote backups copying script. Version $revisionNumber Usage: safecopy [-hvi] [-l hostlist_file] [-o host] -h - this (very useful) help screen -v - output each log line also at terminal -l hostlist_file - specifies hostlist location and filename When missed, use file cphosts in [sysdir/maint] dir -o host - will copied only specified host -i - when first-time login did not done yet, do it now !EOM } # Main logging function # Input: logline (string) - logged string # Output: none safe_logger() { logdate=`date +"%d/%m/%Y %T"` echo "$logdate [$$] safering: $logline" >> $logdir/$logfile # When verbose logging specified, output also at terminal if [ $verbose -eq 1 ]; then echo "$logdate [$$] safering: $logline" fi } # Go down function # Input: godown (string) - directory name to go down # Output: none go_down() { if [ ! -e $godown ]; then mkdir $godown chown $daily_backup_owner:$daily_backup_group $godown chmod $daily_backup_dirmode $godown fi cd $godown } # Detect SSH version by version string. # Input: wsv (string) - output from `ssh -V` command # Output: openssh (string) - "SSH2" when SSH2 detected # "OpenSSH" when OpenSSH detected # "unknown" when any another version detected ssh_version_detect() { set $wsv wsx=${1%%_*} if [ $wsx = "OpenSSH" ]; then openssh="OpenSSH" else if [ $3 = "Secure" ]; then openssh="SSH2" else openssh="unknown" fi fi } # If there is a global system configuration file, suck it in. # if [ -r /etc/defaults/periodic.conf ]; then . /etc/defaults/periodic.conf source_periodic_confs fi # Variables # Theie is only maintained variables! # This is a root folder for all subordinated folders sysdir="/usr/local/share/rmbackup" # This is a Et Cetera folder etcdir="/usr/local/etc" # NO CHANGES BEHIND THIS LINE!! YOU HAVE BEEN WARNED!! backupdir="backup" ringdir="$sysdir/backup" maintdir="$sysdir/maint" logdir="$maintdir/logs" hostlist="$etcdir/cphosts" logfile="$maintdir/saferlog" wsyear=`date +"%Y"` wsmon=`date +"%m"` wsday=`date +"%d"` logfile="sysmsg" # To verbose output specify -v in commandline verbose=0 # To do interactive login, when it didn't done, specify -i in commandline interactive=0 # To copy only one host, specify it in -o parameter onlyyou="" # We assumed SSH2 by SSH Com. presence and locating config in /usr/local/etc/ssh2 openssh=0 sshconf="/usr/local/etc/ssh2/ssh2_config" sshome="$HOME/.ssh2" # This is a fake variable, it need only for variable presence, it value ignored # DO NOT TOUCH THEIR NAME! Revision="fake" # Take revision number from CVS substitution # (after committing we will here "$Revision: 1.1.1.1 $") revline="$Revision: 1.1.1.1 $" revisionNumber=`echo $revline | awk '{print $2}'` # Parse a command line args=`getopt l:o:hvi $*`; if [ $? -ne 0 ]; then usage exit 20 fi set -- $args for i in $args do case "$i" in -h) usage exit;; -l) hostlist="$2"; shift; shift;; -o) onlyyou="$2"; shift; shift;; -v) verbose=1; shift;; -i) interactive=1; shift;; --) shift; break;; esac done # Check on presence SSH in system and detect their version # Version detection is doing by 'ssh -V' output wssh=`which ssh` if [ -z $wssh ]; then logline="No any SSH program was detected, install it first"; safe_logger exit fi # Take SSH version and parse their output. Because OpenSSH generate version # like OpenSSH_ver.no., we should strip out version to first check wsv=`$wssh -V 2>&1` ssh_version_detect set $wsv if [ $openssh = "OpenSSH" ]; then sshome="$HOME/.ssh" sshconf="/etc/ssh/ssh_config" wsver="$1 $2 $3" else if [ $openssh = "SSH2" ]; then wsver="$2 $3 $4 $5" else logline="Detected unknown SSH program. Their version is: $wsv"; safe_logger exit fi fi # Log detected version logline="SafeRing ver. $revisionNumber started. Detected version of local SSH: $wsver"; safe_logger # Taking identity file name, drop down comment field identity=`grep IdentityFile $sshconf` set $identity # Check on commenting line. Take first char from first field # When length of first field not 1, we take second field as filename, else third idone=`echo $1 | awk '{print substr($1,1,1)}'` if [ $idone = "#" ]; then if [ ${#1} -eq 1 ]; then idfile=$3 else idfile=$2 fi else idfile=$2 fi # For OpenSSH drop down path from pathname if [ $openssh = "OpenSSH" ]; then idname=${idfile##*/} else idname=$idfile fi # Check on existance identification file. When doesn't - SSH dodn't setup to # work with publickey auth method if [ ! -e $sshome/$idname ]; then logline="Publickey auth method did not configured yet"; safe_logger exit fi # OpenSSH usually placed at system area - /usr/bin and should be located by whereis # SSH2 usually installed from ports and should be located by which :-) wsshex=`whereis ssh` set $wsshex opensshex=$2 ssh2ex=`which ssh` wscpex=`whereis scp` set $wscpex openscpex=$2 scp2ex=`which scp` # Taking hosts list. Host list is in format: hostname hostaddr version # Version should be "SSH2" for SSH2 and "OpenSSH" for OpenSSH newline=" " saveifs=$IFS IFS=$newline # When selected backup only for desired host, we use their name as "list" # else we create real list if [ ${#onlyyou} -ne 0 ]; then hosts=`cat $hostlist | grep $onlyyou | \ awk '{if ($1 == "#") next; else print $0}'` else hosts=`awk '{if ($1 == "#") next; else print $0}' < $hostlist` fi # We should check, if commented desired host if [ ${#$hosts} -eq 0 ]; then logline="Desired host commented, hosts file missing or empty"; safe_logger exit 115 fi cd $ringdir # Doing safering update for host in $hosts do IFS=$saveifs # Parse host line # $1 - hostname, $2 - hostadr, $3 - version set $host fullpath=$backupdir/$wsyear/$wsmon-$wsyear/$wsday-$wsmon-$wsyear # Write host identity in log logfile=$1 logline="Backup host $1 [$2]"; safe_logger # Take list of files to backup if [ $3 = "OpenSSH" ]; then wls=`$opensshex -o 'BatchMode=yes' -q $2 \ "cd $fullpath 2> /dev/null && /bin/ls -1"` else if [ $3 = "SSH2" ]; then wls=`$ssh2ex -o "BatchMode yes" -q $2 \ "cd $fullpath 2> /dev/null && /bin/ls -1"` else logline="Unknown SSH version $3 for host $1, skipping..."; safe_logger IFS=$newline continue fi fi # When list is empty, do nothing (and don't create directories) status=$? if [ $status -ne 0 ]; then # When return code is (67 for SSH2), (255 for OpenSSH), that means none # login attempts was done and host keys exchange did not done if ([ $status -eq 67 ] && [ $openssh="SSH2" ]) || \ ([ $status -eq 255 ] && [ $openssh="OpenSSH" ]); then # We try to interactive login, when keys exchange still failed if [ $interactive -eq 1 ]; then logline="Trying to interactive login at $2..."; safe_logger echo "Type \"exit\", when login will succesfully finished" # Run interactive login shell if [ $3 = "OpenSSH" ]; then $opensshex $2 else $ssh2ex $2 fi printf "\n\nKeys exchange done. please re-run safecopy\n" else logline="Host keys exchagne did not done. Please login first time interactively"; safe_logger fi fi # When return code is 78 for SSH2 that means no key was generated on remote side if ([ $status -eq 78 ] && [ $openssh="SSH2" ]); then logline="Remote user yet not generated their keypair, use ssh-keygen2 first"; safe_logger fi # And, at least, when return code is 2 - that means directory was empty # (we should check retcode here, because here we will be also with retcode # 67, when interactive login was selected) if [ $status -eq 2 ]; then logline="There is nothing to backup"; safe_logger fi IFS=$newline continue else # Go down the ladder godown=$1; go_down godown=$wsyear; go_down godown=$wsmon-$wsyear; go_down godown=$wsday-$wsmon-$wsyear; go_down fi for file in $wls do # When current object use OpenSSH, we will use it now. When current object # use SSH2, we will use it if [ $3 = "SSH2" ]; then $scp2ex -q -Q $2:$fullpath/$file . 2> /dev/null else $openscpex -q -o 'StrictHostKeyChecking=yes' -o 'BatchMode=yes' $2:$fullpath/$file . 2> /dev/null fi status=$? # Check on operation return code if [ $status -ne 0 ]; then logline="Transfer of file $file unsuccesful, return code is $status"; safe_logger else logline="File $file from host $1 was succesfully transferred"; safe_logger chown $daily_backup_owner:$daily_backup_group $file chmod $daily_backup_mode $file fi done # Return to top cd $ringdir IFS=$newline done