--- /dev/null
+.. -*- restructuredtext -*-
+
+
+==========================================
+Bacap - The extremely simple backup script
+==========================================
+
+:Author: Leandro Lucarella
+:Contact: luca@llucax.com.ar
+:Copyright: Leandro Lucarella (2010), released under the BOLA_ license
+
+.. _BOLA: http://blitiri.com.ar/p/bola/
+
+
+
+About
+=====
+
+Bacap_ is a very simple script (~100 SLOC_ of Bash_) to do an incremental backup
+that saves space using rsync_ and hard-links. Is not the first, and it probably
+will not be the last, so why should you use precisely this one? I have **no**
+idea. All I can tell you is:
+
+1. I did it, so it has to be great!
+2. Is very simple, meaning is very easy to understand and customize.
+3. You can backup multiple hosts.
+
+Did I mention is very simple? I guess that is the only selling point, so
+remember: **It's very simple** =)
+
+.. _Bacap: http://www.llucax.com.ar/proj/bacap/
+.. _SLOC: http://en.wikipedia.org/wiki/Source_lines_of_code
+.. _Bash: http://www.gnu.org/software/bash/
+.. _rsync: http://rsync.samba.org/
+
+
+
+Instalation
+===========
+
+Doing something very complex in ~100 SLOC_ is not easy, unless you're standing
+in the shoulders of giants. I'm standing in the shoulders of rsync_ mainly, so
+you should install it before using the script. You will need a bunch of `basic
+POSIX commands`__, like ``date``, ``dirname``, ``readlink``, ``basename``,
+``cat``, ``awk``, etc.; and ``crontab`` if you don't want to run the script
+manually each time you remember to actually do a backup; but I'm sure you
+already have those. And of course, Bash_, but again, I'm sure you have it too.
+If you want to backup remote hosts, be sure ssh_ is installed too.
+
+__ http://www.opengroup.org/onlinepubs/009695399/utilities/toc.html
+.. _ssh: http://www.openssh.com/
+
+Once you have it all, just `download the script`__ from the git_ repo__ and copy
+it to wherever you like. Set the executable bit if appropriate::
+
+ chmod a+x bacap
+
+__ http://git.llucax.com.ar/w/software/bacap.git?a=blob_plain;f=bacap;hb=HEAD
+__ http://git.llucax.com.ar/w/software/bacap.git
+.. _git: http://git-scm.com/
+
+
+
+Configuration
+=============
+
+If you don't like the defaults (you probably wont), you can add a configuration
+file. Configuration files are searched in this places:
+
+1. ``/etc/bacaprc``
+2. ``/etc/bacap/bacaprc``
+3. ``bacaprc`` in the same directory as the ``bacap`` script
+
+Order is important, since all files are read (if possible) and values in the
+last configuration file read overwrites old values. The script takes an optional
+parameter, which is another location to look for a configuration file. The
+configuration file passed as argument will be read last, and an error will be
+printed if can't be found (no error is issued if any of the other configuration
+files are missing).
+
+The configuration file is a Bash_ script too, and these are the default values:
+
+.. include:: bacap
+ :literal:
+ :start-after: #_INCLUDE_START_
+ :end-before: #_INCLUDE_END_
+
+Once you've created the configuration file(s), you should create the directory
+``$CONFIG_PATH`` (meaning, the value you used for that variable in the
+configuration file)::
+
+ mkdir -p $CONFIG_PATH
+
+The create a directory for each host you want to backup there, the directory
+name should be the name of the host (as you would use to connect to it using
+ssh_). For now let's say we will only backup ``localhost``::
+
+ mkdir $CONFIG_PATH/$LOCALHOST
+
+You should be able to guess what ``$LOCALHOST`` stands for by now =)
+
+Now, you should create at least one file there, ``paths`` which should have one
+line for each path to backup in that host. Let's say we want to backup only
+``/etc`` and ``/home``::
+
+ echo /etc > $CONFIG_PATH/$LOCALHOST/paths
+ echo /home > $CONFIG_PATH/$LOCALHOST/paths
+
+But sometimes there are things there that you don't want to backup, in that
+case you can create a file named ``excludes`` too, and write which paths you
+want to exclude there, one path in each line (you can use wildcards and anything
+supported by the ``--exclude-from`` rsync_ option). Let's say we don't want to
+backup rata's home::
+
+ echo /home/rata/ > $CONFIG_PATH/$LOCALHOST/excludes
+
+That's pretty much it. If you want to add other hosts, just create the host
+directory and the needed host configuration files.
+
+You may want to automate it using *cron*. I will not include a *cron* tutorial
+here, but if you are completely lost, you can add this line to ``/etc/crontab``
+to make a daily backup at 6:30::
+
+ 25 6 * * * root /path/to/bacap
+
+If you are a Debian_ user, you can also simply install the script in
+``/etc/cron.daily`` (or make a symlink or something similar) and you are set.
+
+.. _Debian: http://www.debian.org/
+
+
+
+Usage
+=====
+
+As we said in the configuration section, the only argument the script take is an
+extra configuration file. All options are managed through configurations files.
+
+The script creates a new directory in ``$BACKUP_PATH/$host/$date`` and copies
+(hard-links) the configured paths for each ``$host``. ``$date`` is the current
+date at the time of starting the script, formated according to ``$DATE_FMT``. By
+default this has day *resolution*, but you can add hours, minutes or even
+seconds if you want to do more frequent backups. If the directory already exist
+for any host, it skips that host.
+
+A symbolic link is created at the end of the backup, with the name
+``$BACKUP_PATH/$host/current``, and pointing to the newly created directory.
+
+
+
+Similar alternatives
+====================
+
+* Do it yourself: this script was **heavily** inspired by an article__ by `Kevin
+ Korb`__ (well, it was really inspired by the `previous version`__ of the
+ article =).
+* `Back In Time`__: Nice looking graphical alternative.
+* `rsnapshot`__: A more mature and heavier alternative. I didn't really used it
+ though, so I can't say much.
+
+__ http://www.sanitarium.net/golug/rsync_backups_2010.html
+__ http://www.sanitarium.net/
+__ http://www.sanitarium.net/golug/rsync_backups_2005.html
+
+__ http://backintime.le-web.org/
+
+__ http://rsnapshot.org/
+
+
+I'm sure there are plenty of others, if you have one and want to be listed here,
+please feel free to `drop me an e-mail`__.
+
+__ mailto:luca@llucax.com.ar
+
+
--- /dev/null
+#!/bin/bash
+
+#_INCLUDE_START_
+# Default config values
+
+# Be verbose
+VERBOSE=1
+
+# Be extra verbose
+DEBUG=0
+
+# Don't actually do anything, just print the commands
+DRY_RUN=0
+
+# Log file (if empty, print to stdout/err)
+LOG_FILE=
+
+# Where to find the configuration of the hosts to backup
+CONFIG_PATH=/etc/bacap/hosts
+
+# Name of the local host (so no ssh would be used with this host)
+LOCALHOST=localhost
+
+# Where to put the backups
+BACKUP_PATH=/backup
+
+# Date format used for backed up directories (passed to the date command)
+DATE_FMT="%Y-%m-%d"
+
+# rsync flags to use
+RSYNC_FLAGS="-aAXHx --numeric-ids --delete"
+
+# rsync flags to use when in verbose mode
+RSYNC_VERBOSE_FLAGS="-v --stats"
+
+# rsync remote shell to use
+RSYNC_RSH="ssh -c arcfour -o Compression=no -x"
+
+#_INCLUDE_END_
+
+
+# Load configuration files
+source "/etc/bacaprc" 2> /dev/null
+source "/etc/bacap/bacaprc" 2> /dev/null
+source `dirname \`readlink -f "$0"\``/bacaprc 2> /dev/null
+test -n "$1" && source "$1"
+
+
+export RSYNC_RSH
+
+run=
+[ $DRY_RUN -eq 1 ] &&
+ run=echo
+[ $VERBOSE -eq 1 ] &&
+ RSYNC_FLAGS="$RSYNC_FLAGS $RSYNC_VERBOSE_FLAGS"
+[ $DEBUG -eq 1 ] &&
+ V=-v
+[ -n "$LOG_FILE" ] &&
+ exec 3>&2 &&
+ exec 1>>"$LOG_FILE" &&
+ exec 2>>"$LOG_FILE"
+
+pout() {
+ echo "$@" >&3
+}
+
+plog() {
+ [ $VERBOSE -eq 1 ] &&
+ echo "$@"
+}
+
+perror() {
+ echo "$@" >&2
+}
+
+host_up() {
+ ping -c1 "$1" > /dev/null 2>&1
+}
+
+date=`date "+$DATE_FMT"`
+plog
+plog
+plog "========================================================================="
+plog "Starting backup for $date at `date '+%Y-%m-%d %H:%M:%S'`"
+plog "========================================================================="
+ret=0
+for host_path in "$CONFIG_PATH"/*
+do
+ host=`basename "$host_path"`
+ host_backup_path="$BACKUP_PATH/$host"
+ dst="$BACKUP_PATH/$host/$date"
+ src=`cat "$host_path/paths"`
+ [ "$host" != "$LOCALHOST" ] &&
+ src=`awk "{print \"$host:\"\\$1}" "$host_path/paths"`
+ exclude="$host_path/excludes"
+ current_link="$host_backup_path/current"
+ current_dir="$host_backup_path/`readlink \"$current_link\"`"
+ exclude_flags=
+ plog "-----------------------------------------------------------------"
+ plog "Backup for host $host"
+ plog "-----------------------------------------------------------------"
+ plog "Source: "$src
+ plog "Destination: $dst"
+ plog "Last: $current_dir"
+ plog
+ [ -d "$dst" ] &&
+ perror "$dst already exists, skipping..." &&
+ continue
+ ! host_up $host &&
+ perror "$host is down, skipping..." &&
+ continue
+ [ -r "$exclude" ] &&
+ exclude_flags=" --exclude-from=$exclude --delete-excluded"
+ plog "Rotating backup..."
+ $run cp -al $V "$current_dir" "$dst" ||
+ ret=$(($ret+1))
+ plog "Running rsync..."
+ $run rsync $RSYNC_FLAGS $exclude_flags $src "$dst/" ||
+ ret=$(($ret+1))
+ plog "Moving current..."
+ $run rm $V "$current_link" ||
+ ret=$(($ret+1))
+ $run ln -s $V "$date" "$current_link" ||
+ ret=$(($ret+1))
+done
+
+plog "========================================================================="
+plog "Backup for $date finished at `date '+%Y-%m-%d %H:%M:%S'`"
+plog "========================================================================="
+
+if [ $ret -ne 0 ]
+then
+ pout 'There were some errors when running the backup.'
+ pout
+ pout "Please take a look at the log: $LOG_FILE"
+ pout
+fi
+
+exit $ret
+