--- /dev/null
+# -*- mode: sh; sh-basic-offset: 3; indent-tabs-mode: nil; -*-
+# vim: set filetype=sh sw=3 sts=3 expandtab autoindent:
+
+defined_backends=()
+available_backends=()
+
+function init_backends () {
+ for backend in "${defined_backends[@]}"; do
+ . "$libdirectory/backends/$backend"
+ if "backend_${backend}_init"; then
+ append available_backends "$backend"
+ debug "Backend '$backend' is available on this system"
+ else
+ debug "Backend '$backend' is not available on this system"
+ fi
+ done
+}
+
+function init_source_hosts () {
+ # Use our first argument as the name of a configuration parameter to
+ # load the hosts from, or use the "hosts" parameter if nothing was
+ # passed. If the parameter is empty, use "localhost".
+ getconf_words "hosts" "local:"
+ # Allow the default backend variable to be overridden in this
+ # handler's configuration, but fall back to the global default
+ # backend. We use printconf instead of getconf to prevent the global
+ # $default_backend from getting clobbered.
+ local_default_backend=`printconf "default_backend" $default_backend`
+
+ # Check to see if the default backend is valid (don't check
+ # availability yet, though)
+ if [ -n "$local_default_backend" ] && ! backend_defined "$local_default_backend"; then
+ fatal "Default backend '$local_default_backend' does not exist"
+ fi
+
+ local hostspec
+ for hostspec in "${hosts[@]}"; do
+
+ # Find the backend
+ local backend=`backend_for_hostspec "$hostspec"`
+ if [ -z "$backend" ]; then
+ if [ -z "$local_default_backend" ]; then
+ fatal "No default backend set and no explicit backend in hostspec '$hostspec'"
+ else
+ backend=$local_default_backend
+ fi
+ fi
+
+ # Check if the backend is valid and available
+ if ! backend_defined "$backend"; then
+ fatal "Backend '$backend' does not exist"
+ elif ! backend_available "$backend"; then
+ fatal "Backend '$backend' is not available on this system"
+ fi
+
+ # Check if the host is available
+ local reason
+ local host=`host_for_hostspec "$hostspec"`
+ if ! "backend_${backend}_host_available" "$host" reason; then
+ fatal "Host '$hostspec' is not available: $reason"
+ fi
+
+ # Everything is ok, we can run on this host!
+ done;
+}
+
+# Run the function pointed to by $1 for each of the source hosts defined
+# in the configuration. Sets up the current backend and source host
+# variables, so source_run & friends can be used. Also sets up the $root
+# variable to point to the current source host's root filesystem (with
+# no trailing /).
+# This function should only be used after calling init_source_hosts.
+function run_for_source_hosts () {
+ # $hosts will have been filled by init_source_hosts
+ for hostspec in "${hosts[@]}"; do
+ current_backend=`backend_for_hostspec "$hostspec"`
+ current_host=`host_for_hostspec "$hostspec"`
+ # Set the current source's root.
+ root=`"backend_${current_backend}_host_root" "$current_host"`
+
+ info "Running on $current_backend:$current_host"
+
+ # Run the actual backup handler
+ "$1"
+ done
+}
+
+# Run the given command on the current source host.
+# The command should be $1, with $2, $3, etc. being its arguments.
+source_run () {
+ "backend_${current_backend}_run" "$@"
+}
+
+function backend_available () {
+ in_array "$1" "${available_backends[@]}"
+}
+
+function backend_defined () {
+ in_array "$1" "${defined_backends[@]}"
+}
+
+function backend_for_hostspec () {
+ echo "$1" | sed 's/^\(\([^:]*\):\)\?\(.*\)$/\2/'
+}
+
+function host_for_hostspec () {
+ echo "$1" | sed 's/^\(\([^:]*\):\)\?\(.*\)$/\3/'
+}
+
+
+# Replaces escape sequences in $1 with the proper values for the current
+# backend.
+# The result is put on stdout. If $2 is empty, values for the host are
+# replaced. The following values are replaced:
+#
+# %% Literal %
+# %h The short hostname (as returned by hostname -s)
+# %H The full hostname (as returned by hostname --fqdn)
+# %n The vserver name, or empty for the host
+# %N The vserver name, or "host" for the host
+# %v The vserver root directory, or empty for the host
+#
+# Note that the given vserver must be running!
+#
+function interpolate() {
+ path=$1
+
+ expr=''
+ # Allow backends to supply extra vars
+ vars=`"backend_${current_backend}_interpolate_vars"`
+ vars="h H s S r $vars"
+ for var in $vars; do
+ # Do indirect lookup of the value to replace
+ val=`interpolate_value "$var"`
+ # Escape slashes, backslashes and ampersands, since those have
+ # special meaning for sed (note that we need to double the \
+ # twice, since it has special meaning to bash in the val=
+ # assignment, but also to sed.
+ val=`echo "$val" | sed 's#[/&\\\\]#\\\\&#g'`
+ # Add replacement pattern. The first part checks that there is
+ # an odd number of percent signs before the variable, so
+ # double % is properly left alone.
+ expr="${expr}s/\(\(^\|[^%]\)\(%%\)*\)%$var/\1$val/g;"
+ done
+ # Finally replace literal % signs
+ expr="${expr}s/%%/%/g"
+
+ # Do the actual interpolation
+ echo $path | sed "$expr"
+}
+
+function interpolate_value () {
+ case "$1" in
+ h) `source_run hostname -s`;;
+ H) `source_run hostname --fqdn`;;
+ s) echo "$current_host";;
+ S) echo "$current_backend:$current_host";;
+ r) echo "$root";;
+ # Not one of the defaults, ask the backend
+ *) "backend_${current_backend}_interpolate_value" "$current_host" "$1"
+ esac
+}