diff options
Diffstat (limited to 'lbu.in')
-rw-r--r-- | lbu.in | 554 |
1 files changed, 554 insertions, 0 deletions
@@ -0,0 +1,554 @@ +#!/bin/sh + +# lbu - utility to create local backups. +# Copyright (c) 2006 Natanael Copa +# May be distributed under GPL2 + +VERSION=2.0_alpha7 +sysconfdir=@sysconfdir@ + +if [ ! -f ${libalpine:="./libalpine.sh"} ]; then + libalpine=/usr/share/lbu/libalpine.sh + if [ ! -f "$libalpine" ]; then + libalpine=/lib/libalpine.sh + fi +fi +. $libalpine || exit 1 + +EXCLUDE_LIST="$sysconfdir"/exclude +INCLUDE_LIST="$sysconfdir"/include + +DEFAULT_CIPHER="aes-256-cbc" + +LBU_CONF="$sysconfdir"/lbu.conf +if [ -f "$LBU_CONF" ]; then + . "$LBU_CONF" +fi + +UMOUNT_LIST= + +usage() { + echo "$PROGRAM $VERSION" + echo "usage: $PROGRAM <subcommand> [options] [args] + +Available subcommands: + commit (ci) + exclude (ex, delete) + include (inc, add) + list (ls) + package (pkg) + status (stat, st) + list-backup (lb) + revert + +Common options: + -h Show help for subcommand. + -q Quiet mode. + -v Verbose mode. +" + exit 1 +} + +cleanup() { + local i + for i in $UMOUNT_LIST; do + umount $i + done +} + +exit_clean() { + cleanup + exit 1 +} + +mount_once() { + if ! grep $1 /proc/mounts >/dev/null; then + mount $1 && UMOUNT_LIST="$1 $UMOUNT_LIST" + fi +} + +# create backupfile +backup_apkovl() { + local outfile="$1" + local d=$( date -u -r "$outfile" "+%Y%m%d%H%M%S" ) + local backup=$(echo "$outfile" | sed "s/\.apkovl\.tar\.gz/.$d.tar.gz/") + vecho "Creating backup $backup" + if [ -z "$DRYRUN" ]; then + mv "$outfile" "$backup" + fi +} + +# verify we have openssl if we want to encrypt +check_openssl() { + [ -z "$ENCRYPTION" ] && return 0 + OPENSSL=$(which openssl 2>/dev/null) || die "openssl was not found" + + $OPENSSL list-cipher-commands | grep "^$ENCRYPTION$" > /dev/null \ + || die "Cipher $ENCRYPTION is not supported" +} + +# list_add(char *listfile, char* file...) +list_add() { + local list="$1" + shift + mkdir -p `dirname "$list"` + while [ $# -gt 0 ] ; do + filename=`echo "$1" | sed 's:^/\+::'` + if grep "^$filename$" "$list" >/dev/null 2>&1 ; then + vecho "$filename is already in $list." + else + vecho "Adding $filename to $list." + echo "$filename" >> "$list" + fi + shift + done +} + +# list_delete(char *listfile, char *file...) +list_delete() { + local list="$1" + local tmp="$list.old" + shift + [ -f "$list" ] || return 1 + while [ $# -gt 0 ] ; do + filename=`echo "$1" | sed 's:^/\+::'` + mv "$list" "$tmp" + vecho "Removing $filename from list." + grep -v "^$filename$" "$tmp" > "$list" + rm "$tmp" + shift + done +} + + + +# +# lbu_include - add/remove files to include list +# +usage_include() { + echo "$PROGRAM $VERSION +Add filename(s) to include list ($sysconfdir/include) + +usage: $PROGRAM include|inc|add [-rv] <file> ... + $PROGRAM include|inc|add [-v] -l + +Options: + -l List contents of include list. + -r Remove specified file(s) from include list instead of adding. + -v Verbose mode. +" + exit 1 +} + +cmd_include() { + if [ "$LIST" ] ; then + [ $# -gt 0 ] && usage_include + show_include + return + fi + + [ $# -lt 1 ] && usage_include + if [ "$REMOVE" ] ; then + list_delete "$INCLUDE_LIST" "$@" + else + list_add "$INCLUDE_LIST" "$@" + list_delete "$EXCLUDE_LIST" "$@" + fi +} + +show_include() { + if [ -f "$INCLUDE_LIST" ] ; then + vecho "Include files:" + cat "$INCLUDE_LIST" + fi +} + +# +# lbu_package - create a package +# +usage_package() { + echo "$PROGRAM $VERSION +Create backup package. + +usage: $PROGRAM package|pkg -v [<dirname>|<filename>] + +Options: + -v Verbose mode. + +If <dirname> is a directory, a package named <hostname>.apkovl.tar.gz will +be created in the specified directory. + +If <filename> is specified, and is not a direcotry, a package with the +specified name willbe created. + +If <dirname> nor <filename> is not specified, a package named +<hostname>.apkovl.tar.gz will be created in current work directory. +" + exit 1 +} + +cmd_package() { + local pkg="$1" + local rc=0 + local owd="$PWD" + local suff="apkovl.tar.gz" + local tmpdir tmppkg + + check_openssl + init_tmpdir tmpdir + + [ -n "$ENCRYPTION" ] && suff="$suff.$ENCRYPTION" + + # find filename + if [ -d "$pkg" ] ; then + pkg="$pkg/$(hostname).$suff" + elif [ -z "$pkg" ]; then + pkg="$PWD/$(hostname).$suff" + fi + + tmppkg="$tmpdir/$(basename $pkg)" + + cd "${ROOT:-/}" + currentlist=$(apk audit --backup -q) + if [ -f var/lib/apk/world ]; then + currentlist="$currentlist var/lib/apk/world" + fi + + # create tar archive + [ -f "$EXCLUDE_LIST" ] && excl="-X $EXCLUDE_LIST" + [ -f "$INCLUDE_LIST" ] && incl="-T $INCLUDE_LIST" + if [ -n "$VERBOSE" ]; then + echo "Archiving the following files:" >&2 + # we dont want to mess the tar output with the + # password prompt. Lets get the tar output first. + tar $excl $incl -c -v $currentlist > /dev/null + rc=$? + fi + if [ $rc -eq 0 ]; then + if [ -z "$ENCRYPTION" ]; then + tar $excl $incl -c $currentlist | gzip -c >"$tmppkg" + rc=$? + else + set -- enc "-$ENCRYPTION" -salt + [ -n "$PASSWORD" ] && set -- "$@" -pass pass:"$PASSWORD" + tar $excl $incl -c $currentlist | gzip -c \ + | $OPENSSL "$@" > "$tmppkg" + rc=$? + fi + fi + cd "$owd" + + # actually commit unless dryrun mode + if [ $rc -eq 0 ]; then + if [ -z "$DRYRUN" ]; then + if [ "x$pkg" = "x-" ]; then + cat "$tmppkg" + else + cp "$tmppkg" "$pkg" + fi + fi + vecho "Created $pkg" + fi + return $rc +} + +# +# lbu list - list files that would go to archive +# +usage_list() { + echo "$PROGRAM $VERSION +Lists files that would go to tar package. Same as: 'lbu package -v /dev/null' + +usage: $PROGRAM list|ls +" + exit 1 +} + +cmd_list() { + VERBOSE="-v" + cmd_package /dev/null +} + +# +# lbu_commit - commit config files to writeable media +# +usage_commit() { + echo "$PROGRAM $VERSION +Create a backup of config to writeable media. + +usage: $PROGRAM commit|ci [-nv] [<media>] + +Options: + -d Remove old apk overlay files. + -e Protect configuration with a password. + -n Don't commit, just show what would have been commited. + -p <password> Give encryption password on the command-line + -v Verbose mode. + +The following values for <media> is supported: floppy usb +If <media> is not specified, the environment variable LBU_MEDIA will be used. + +Password protection will use $DEFAULT_CIPHER encryption. Other ciphers can be +used by setting the DEFAULT_CIPHER or ENCRYPTION environment variables. +For possible ciphers, try: openssl -v + +The password used to encrypt the file, can either be specified with the -p +option or using the PASSWORD environment variable. + +The environment varialbes can also be set in $LBU_CONF +" + exit 1 +} + +cmd_commit() { + local media mnt statuslist tmplist currentlist + local incl excl outfile ovls lines + + check_openssl + + # turn on verbose mode if dryrun + [ -n "$DRYRUN" ] && VERBOSE="-v" + + # find what media to use + media="${1:-$LBU_MEDIA}" + [ -z "$media" ] && usage_commit + + # mount media unles its already mounted + mnt=/media/$media + [ -d "$mnt" ] || usage + mount_once "$mnt" || die "failed to mount $mnt" + + # find the outfile + outfile="$mnt/$(hostname).apkovl.tar.gz" + if [ -n "$ENCRYPTION" ]; then + outfile="$outfile.$ENCRYPTION" + fi + + + # remove old config files + if [ -n "$DELETEOLDCONFIGS" ] ; then + local rmfiles=$(ls "$mnt/"*.apkovl.tar.gz* 2>/dev/null) + if [ -n "$rmfiles" ] ; then + if [ -n "$VERBOSE" ]; then + echo "Removing old apk overlay files:" >&2 + echo "$rmfiles" + echo "" >&2 + fi + [ -z "$DRYRUN" ] && rm "$mnt/"*.apkovl.tar.gz* + fi + else + lines=$(ls -1 "$mnt"/*.apkovl.tar.gz* 2>/dev/null) + if [ "$lines" = "$outfile" ]; then + backup_apkovl "$outfile" + elif [ -n "$lines" ]; then + # More then one apkovl, this is a security concern + cleanup + eecho "The following apkovl file(s) were found:" + eecho "$lines" + eecho "" + die "Please use -d to replace." + fi + fi + + # create package + if ! cmd_package "$outfile"; then + cleanup + die "Problems creating archive. aborting" + fi + + # delete old backups if needed + # poor mans 'head -n -N' done with awk. + ls "$mnt"/$(hostname).[0-9][0-9][0-9][0-9]*[0-9].tar.gz 2>/dev/null \ + | awk '{ a[++i] = $0; } END { + print a[0]; + while (i-- > '"${BACKUP_LIMIT:-0}"') { + print a[++j] + } + }' | xargs rm 2>/dev/null + + # remove obsolete file. some older version of alpine needs this + # to be ble to upgrade + if [ -z "$DRYRUN" ] && [ -f $mnt/packages.list ]; then + echo "Note: Removing packages.list from $(basename $mnt)." + echo " $PACKAGES_LIST will be used." + rm -f $mnt/packages.list + fi + + # make sure data is written + sync + [ "$media" = "floppy" ] && sleep 1 + + # move current to commited. + vecho "Successfully saved apk overlay files" +} + +#--------------------------------------------------------------------------- +# lbu_exclude - add remove file(s) from exclude list + +usage_exclude() { + echo "$PROGRAM $VERSION +Add filename(s) to exclude list ($sysconfdir/exclude) + +usage: $PROGRAM exclude|ex|delete [-rv] <file> ... + $PROGRAM exclude|ex|delete [-v] -l + +Options: + -l List contents of exclude list. + -r Remove specified file(s) from exclude list instead of adding. + -v Verbose mode. +" + exit 1 +} + +cmd_exclude() { + if [ "$LIST" ] ; then + [ $# -gt 0 ] && usage_exclude + show_exclude + return + fi + + [ $# -lt 1 ] && usage_exclude + if [ "$REMOVE" ] ; then + list_delete "$EXCLUDE_LIST" "$@" + else + list_delete "$INCLUDE_LIST" "$@" + list_add "$EXCLUDE_LIST" "$@" + fi +} + +show_exclude() { + if [ -f "$EXCLUDE_LIST" ] ; then + vecho "Exclude files:" + cat "$EXCLUDE_LIST" + fi +} + +#--------------------------------------------------------------------------- +# lbu_listbackup - Show old commits +usage_listbackup() { + cat <<EOF +$PROGRAM $VERSION +Show old commits. + +usage: $PROGRAM list-backup [<media>] + +EOF + exit 1 +} + +cmd_listbackup() { + local media=${1:-"$LBU_MEDIA"} + local mnt="/media/$media" + [ -z "$media" ] && usage_listbackup + + mount_once "$mnt" || die "failed to mount $mnt" + ls -1 "$mnt"/*.[0-9][0-9]*[0-9][0-9].tar.gz* 2>/dev/null | sed 's:.*/::' +} + +#--------------------------------------------------------------------------- +# lbu_revert - revert to old config +usage_revert() { + cat <<EOF +$PROGRAM $VERSION +Revert to older commit. + +usage: $PROGRAM revert <REVISION> [<media>] + +The revision should be one of the files listed by 'lbu list-backup'. + +EOF +} + +cmd_revert() { + local media=${2:-"$LBU_MEDIA"} + [ -z "$media" ] && usage_revert + local mnt="/media/$media" + local revertto="$mnt/$1" + local current="$mnt/$(hostname).apkovl.tar.gz" + + if [ -n "$ENCRYPTION" ]; then + current="$current.$ENCRYPTION" + fi + mount_once "$mnt" || die "failed to mount $mnt" + [ -f "$revertto" ] || die "file not found: $revertto" + backup_apkovl "$current" + vecho "Reverting to $1" + [ -z "$DRYRUN" ] && mv "$revertto" "$current" +} + +#--------------------------------------------------------------------------- +# lbu_status - check what files have been changed since last save +usage_status() { + echo "$PROGRAM $VERSION +Check what files have been changed since last commit. + +usage: $PROGRAM status|st [-av] + +Options: + -a Compare all files, not just since last commit. + -v Also show include and exclude lists. +" + exit 1 +} + + + +#----------------------------------------------------------- +# Main + +cmd=`echo "$PROGRAM" | cut -s -d_ -f2` +PROGRAM=`echo "$PROGRAM" | cut -d_ -f1` +if [ -z "$cmd" ] ; then + cmd="$1" + [ -z "$cmd" ] && usage + shift +fi + +# check for valid sub command +case "$cmd" in + include|inc|add) SUBCMD="include";; + commit|ci) SUBCMD="commit";; + exclude|ex|delete) SUBCMD="exclude";; + list|ls) SUBCMD="list";; + package|pkg) SUBCMD="package";; + status|stat|st) SUBCMD="status";; + list-backup|lb) SUBCMD="listbackup";; + revert) SUBCMD="revert";; + *) usage;; +esac + +# parse common args +while getopts "adehlM:np:qrv" opt ; do + case "$opt" in + a) [ $SUBCMD = status ] || usage_$SUBCMD + USE_DEFAULT="-a" + ;; + d) DELETEOLDCONFIGS="yes" + ;; + e) [ -z "$ENCRYPTION" ] && ENCRYPTION="$DEFAULT_CIPHER" + ;; + h) usage_$SUBCMD + ;; + l) LIST="-l" + ;; + n) [ $SUBCMD = commit ] || usage_$SUBCMD + DRYRUN="-n" + ;; + p) PASSWORD="$OPTARG" + ;; + q) QUIET="$QUIET -q" + ;; + r) REMOVE="-r" + ;; + v) VERBOSE="$VERBOSE -v" + ;; + esac +done +shift `expr $OPTIND - 1` + +trap exit_clean SIGINT SIGTERM +cmd_$SUBCMD "$@" +retcode=$? + +cleanup +exit $retcode |