#!/bin/sh -e # Copyright (C) 2006-2008 David Mandelberg # use ssh-agent test x"$USING_SSH_AGENT" = x"yes" || USING_SSH_AGENT=yes exec ssh-agent "$0" "$@" BASE="`dirname "$0"`" METARELDIR="metadata" METADIR="$BASE/$METARELDIR" RS="_rsync -ua --delete" SSH="_ssh" PROXY="" PROXY_PORT=1080 PROXY_TIMEOUT=5 QUIET=0 VERBOSE=0 REMOTE=0 NAME="`basename "$0"`" print_help () { printf "$NAME [OPTION...] [COMMAND...]\n" printf "\tOPTIONS:\n" printf "\t\t-h | --help: print a help message\n" printf "\t\t-u | --usage: print a usage message\n" printf "\t\t-p | --proxy: use an ssh proxy\n" printf "\t\t-q | --quiet: print less\n" printf "\t\t-v | --verbose: print more\n" printf "\t\t-l KBPS | --limit KBPS: limit bandwidth usage\n" printf "\t\t-o host1[,host2...] | --only host1[,host2...]: only backup certain hosts\n" printf "\t\t-n host1[,host2...] | --not host1[,host2...]: don't backup certain hosts\n" printf "\tCOMMANDS:\n" printf "\t\t-c [SNIPPET]| --cmd [SNIPPET] | --command [SNIPPET]\n" printf "\t\t\truns SNIPPET as a shell snippet after all backup commands and before any upload commands\n" } print_usage () { printf "$NAME [-h|-u]\n" printf "$NAME [-q|-v] [-p ssh_proxy_host] [-l KBPS] [-c snippet]... [-o host1[,host2...]] [-n host1[,host2...]]\n" } msg () { test x"$QUIET" = x0 && echo " * $*" >&2 return 0 } verbose () { test x"$VERBOSE" = x1 && msg "$@" return 0 } error () { echo " * $*" >&2 exit 1 } # echo and run a command run () { msg "$*" "$@" } run_verbose () { verbose "$*" "$@" } # create a temp file c_tmp () { TMP="`mktemp`" verbose "created temporary file: $TMP" } # delete a temp file d_tmp () { rm -f "$TMP" verbose "removed temporary file: $TMP" unset TMP } # move a temp file m_tmp () { mv "$TMP" "$1" verbose "moved temporary file: $TMP -> $1" unset TMP } # if passed /foo/bar/baz, create /foo and /foo/bar mkcontdirs () { while true; do if test ! -e "$1"; then run_verbose mkcontdirs "`dirname "$1"`" run mkdir "$1" fi shift || break test $# -lt 1 && break done } stop_remote () { run_verbose ssh-add -D REMOTE=0 } forced_quit () { msg "Quitting..." test $REMOTE = 1 && stop_remote exit 1 } trap forced_quit 1 2 3 6 15 setup_remote () { REMOTE=1 run_verbose ssh-add } host_in_list () { _HOST="$1" shift _LIST="$1" test x"$_LIST" = x && _LIST="$HOSTS" test x"$_LIST" = x && return 1 _HOSTINLIST=1 for i in $_LIST; do case "$i" in "@ALL@"|"$_HOST") _HOSTINLIST=0 ;; "-@ALL@"|"-$_HOST") _HOSTINLIST=1 ;; esac done return $_HOSTINLIST } _is_mounted () { grep -q " $_MTPT " /etc/mtab || return 1 return 0 } _mount () { _MTPT="$1"; shift # did this program mount it or was it already mounted? # 0 = already mounted; 1 = this program OURMOUNT=0 if _is_mounted; then OURMOUNT=0 verbose "$_MTPT already mounted." else OURMOUNT=1 run_verbose mount "$_MTPT" fi } _umount () { _MTPT="$1"; shift if test x"$OURMOUNT" = x0; then verbose "Not unmounting $_MTPT." else run_verbose umount "$_MTPT" fi } bk_prep () { _NAME="$1"; shift verbose "Preparing $_NAME" run_verbose mkcontdirs "$BASE/$_NAME" for i in "$@"; do run_verbose mkcontdirs "$BASE/$_NAME/$i" done } # handle ssh proxy start_proxy () { run_verbose ssh -fD "$PROXY_PORT" "$PROXY" sleep "$PROXY_TIMEOUT" } stop_proxy () { test -n "$PROXY" || return test -f "$TSOCKS_CONF_FILE" && rm "$TSOCKS_CONF_FILE" } _rsync () { if test -n "$PROXY"; then start_proxy tsocks rsync "$@" else rsync "$@" fi } _ssh () { if test -n "$PROXY"; then start_proxy tsocks ssh "$@" else ssh "$@" fi } bk_remote () { NAME="$1"; shift HOST="$1"; shift RSOPTS="$1"; shift DATE="`date`" host_in_list "$NAME" || return 0 msg "Backing up $NAME ($HOST)" bk_prep "$NAME" "$@" c_tmp run_verbose "$SSH" "$HOST" "dpkg --get-selections" > "$TMP" m_tmp "$METADIR/$NAME.packages" c_tmp printf "%s" "$DATE" > "$TMP" for LOC in "$@"; do run_verbose $RS $RSOPTS "$HOST:/$LOC" "$BASE/$NAME/$LOC" done m_tmp "$METADIR/$NAME.time" } bk_local () { NAME="$1"; shift RSOPTS="$1"; shift DATE="`date`" host_in_list "$NAME" || return 0 msg "Backing up $NAME (local machine)" bk_prep "$NAME" "$@" c_tmp hostname > "$TMP" m_tmp "$METADIR/$NAME.hostname" c_tmp run_verbose dpkg --get-selections > "$TMP" m_tmp "$METADIR/$NAME.packages" c_tmp printf "%s" "$DATE" > "$TMP" for LOC in "$@"; do run_verbose $RS $RSOPTS "/$LOC" "$BASE/$NAME/$LOC" done m_tmp "$METADIR/$NAME.time" } bk_media () { NAME="$1"; shift MTPT="$1"; shift RSOPTS="$1"; shift DATE="`date`" host_in_list "$NAME" || return 0 msg "Backing up $NAME (removable media)" bk_prep "$NAME" "$@" c_tmp printf "%s" "$DATE" > "$TMP" _mount "$MTPT" for LOC in "$@"; do run_verbose $RS $RSOPTS "$MTPT/$LOC" "$BASE/$NAME/$LOC" done _umount "$MTPT" m_tmp "$METADIR/$NAME.time" } bk_upload () { NAME="upload-$1"; shift host_in_list "$NAME" || return 0 LOC="$1"; shift msg "Uploading backup to $LOC ($NAME)" c_tmp printf "%s" "$DATE" > "$TMP" run_verbose $RS "$BASE/" "$LOC/" m_tmp "$METADIR/$NAME.time" run $RS "$METADIR/$NAME.time" "$LOC/$METARELDIR/$NAME.time" } # parse options TMP="`getopt -o c:huqo:n:vp:l: -l cmd:,command:,help,usage,quiet,only:,not:,verbose,proxy:,limit: -n "$NAME" -- "$@"`" eval set -- "$TMP" CMDS="true" HOSTS="@ALL@" while true; do case "$1" in -c|--cmd|--command) CMDS="$CMDS; $2" shift 2 ;; -o|--only) HOSTS="`printf "%s" "$2" | sed 's/,/ /g'`" shift 2 ;; -n|--not) HOSTS="$HOSTS -`printf "%s" "$2" | sed 's/,/ -/g'`" shift 2 ;; -p|--proxy) PROXY="$2" shift 2 export TSOCKS_CONF_FILE="`mktemp`" echo "server = 127.0.0.1" >> "$TSOCKS_CONF_FILE" echo "server_port = $PROXY_PORT" >> "$TSOCKS_CONF_FILE" ;; -l|--limit) RS="$RS --bwlimit=$2" shift 2 ;; -h|--help) print_help exit ;; -u|--usage) print_usage exit ;; -q|--quiet) QUIET=1 SSH="$SSH -q" RS="$RS -q" shift ;; -v|--verbose) VERBOSE=1 RS="$RS -v --progress" shift ;; --) shift break ;; *) error "Error parsing options." esac done setup_remote bk_remote www-server www.foo.example.com \ "" \ etc/ \ root/ \ home/ \ var/spool/cron/crontabs/ \ var/www/ bk_remote workstation1 workstation1.example.com \ "--delete-excluded --exclude /*/.mozilla/firefox/*/Cache/*" \ etc/ \ root/ \ home/ \ var/spool/cron/crontabs/ bk_local desktop \ "--delete-excluded --exclude /*/.cache/*/** --exclude /*/.mozilla/firefox/*/Cache/*" \ etc/ \ root/ \ home/ \ var/www/ \ var/spool/cron/crontabs/ bk_media ipod /media/IPOD \ "" \ ./ $CMDS bk_upload backup-server backups.example.com:/media/backup stop_remote stop_proxy msg "Backup complete!"