#!/bin/bash # # ppsnapback-install - ppsnapback installation wizard # # Copyright (C) 2011 Nicolas Schodet. # # See end of file for documentation, copyright and license. # # Man page can be generated using: # # $ pod2man -c '' -r '' ppsnapback-install > ppsnapback-install.1 # fatal () { echo $* >&2 exit 1 } usage () { fatal "Usage: $0 [options] MODULE [BACKUP_DIR]" } ask () { local question=$1 local default=$2 local validate=$3 local response [[ -n $default ]] && question="$question [$default]" while true; do echo -n "$question " >&2 read response [[ -z $response ]] && response="$default" [[ -z $validate ]] && break response=$($validate "$response") && break done echo "$response" } validate_yesno () { case "$1" in [yY]|[yY][eE][sS]) echo yes ;; [nN]|[nN][oO]) echo no ;; *) echo "please answer yes or no" >&2 return 1 ;; esac return 0 } # Check parameter and check configuration does not exists. btype=push bpath= hostname=ASK remote= rppsnapback= while getopts 't:p:h:r:R:' option; do case "$option" in t) btype="$OPTARG" ;; p) bpath="$OPTARG" ;; h) hostname="$OPTARG" ;; r) remote="$OPTARG" ;; R) rppsnapback="$OPTARG" ;; ?) usage ;; esac done shift $((OPTIND-1)) MODULE=${1:-NONE} BASEDIR=${2:-$HOME/backups} MD=$BASEDIR/$MODULE [[ $MODULE = NONE ]] && usage [[ ! -d $BASEDIR ]] && fatal "backup directory '$BASEDIR' does not exist" [[ -e $MD ]] && fatal "module directory for '$MODULE' exists" # Cleanup code. cleanup () { echo "cleaning up..." [[ -f $MD/config ]] && rm "$MD/config" [[ -f $MD/exclude ]] && rm "$MD/exclude" [[ -f $MD/$MODULE-backup-key ]] && rm "$MD/$MODULE-backup-key" [[ -f $MD/$MODULE-backup-key.pub ]] && rm "$MD/$MODULE-backup-key.pub" [[ -d $MD ]] && rmdir "$MD" } # Request functions. validate_path () { if [[ ! -d $1 ]]; then echo "path does not exist" >&2 return 1 fi echo "$1" } need_path () { [[ -d $bpath ]] && return bpath=$(ask "Path to backup?" "$PWD/" validate_path) } need_hostname () { [[ $hostname = ASK ]] || return local use_hostname=$(ask "Use from= in SSH authorized_keys?" yes \ validate_yesno) if [[ $use_hostname = yes ]]; then hostname=$(ask "Hostname?" "$(hostname -f)") else hostname= fi } validate_remote () { case "$1" in *:*) echo "remote should not include path, only host" >&2 return 1 ;; '') echo "please enter something" >&2 return 1 ;; esac echo "$1" } need_remote () { if [[ -n $remote ]]; then remote=$(validate_remote "$remote") || exit 1 else remote=$(ask "Remote host?" '' validate_remote) fi } validate_rppsnapback () { case "$1" in /?*) echo "$1" ;; *) echo "please enter absolute path" >&2 return 1 ;; esac } need_rppsnapback () { [[ -n $rppsnapback ]] && return rppsnapback=$(ask "Path to remote ppsnapback script?" \ '/usr/local/bin/ppsnapback' validate_rppsnapback) } # Proceed with installation. case "$btype" in push) need_path need_hostname need_remote need_rppsnapback trap cleanup EXIT mkdir "$MD" || fatal "cannot create module directory" ssh-keygen -t rsa -f "$MD/$MODULE-backup-key" \ -C "${hostname:+$hostname }$MODULE backup key" -N '' \ || fatal "cannot create backup key" cat > "$MD/config" <<-EOF || fatal "cannot write module configuration" path="$bpath" remote="$remote" privkey="$MODULE-backup-key" EOF touch "$MD/exclude" authorized_keys="${hostname:+from=\"$hostname\",}\ command=\"$rppsnapback $MODULE\",\ no-pty,no-port-forwarding,no-x11-forwarding,no-agent-forwarding \ $(< "$MD/$MODULE-backup-key.pub")" RMD="backups/$MODULE" echo $authorized_keys | ssh "$remote" "mkdir \"$RMD\" \ && touch \"$RMD/config\" \ && cat >> .ssh/authorized_keys" || fatal "cannot configure remote" ;; *) fatal "only push type supported" ;; esac trap - EXIT exit 0 __END__ =head1 NAME ppsnapback-install - ppsnapback installation wizard =head1 SYNOPSIS B [I] I [I] =head1 DESCRIPTION B will ask for parameters to create a new B module. This include: =over =item * creation of the local configuration, =item * creation of a SSH backup key, =item * creation of the remote configuration, =item * update of remote authorized_keys file. =back =head1 OPTIONS Options can be used to avoid a parameter prompt. If a parameter is missing, B will ask the user to provide it. =over =item B<-t> I Choose configuration type. For the moment, the only syported type is I. =item B<-p> I Path to backup. Will default to current directory. =item B<-h> I Local machine hostname. This is used to limit SSH authorization to the current machine address. Use an empty string to disable. =item B<-r> I Remote machine or location. =item B<-R> I Absolute path to ppsnapback on the remote machine. =back =head1 RESTRICTIONS For the moment, the only supported configuration type is I, which corresponds to the paragraph "Push Remote Backup Repository With Snapshot Rotation" in B man page. Remote backup directory is always $HOME/backups. =head1 SEE ALSO ppsnapback(1) =head1 AUTHOR Nicolas Schodet, http://ni.fr.eu.org/ppsnapback =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 Nicolas Schodet. This program is free software; you can redistribute 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. =cut