diff options
author | Micah Anderson <micah@riseup.net> | 2009-02-19 12:23:29 -0500 |
---|---|---|
committer | Micah Anderson <micah@riseup.net> | 2009-02-19 12:23:29 -0500 |
commit | 539fd1471907422d81626260ea4f1e23e4d398a4 (patch) | |
tree | efe32e02a285fe1d241e16c6701a95a8201d79e7 /src | |
parent | 27a7859c42394a78c16b24f0d08ca28667bb1efa (diff) | |
download | backupninja-539fd1471907422d81626260ea4f1e23e4d398a4.tar.gz backupninja-539fd1471907422d81626260ea4f1e23e4d398a4.tar.bz2 |
move the branches directories up to the root of the repository
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 34 | ||||
-rw-r--r-- | src/Makefile.in | 343 | ||||
-rwxr-xr-x | src/backupninja.in | 583 | ||||
-rwxr-xr-x | src/ninjahelper.in | 268 | ||||
-rwxr-xr-x | src/ninjareport.in | 205 |
5 files changed, 1433 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..3d5bdb8 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,34 @@ +# tyhle vygenerujeme ... +sbin_SCRIPTS = backupninja ninjahelper + +# a proto je taky musíme smazat ... +CLEANFILES = $(sbin_SCRIPTS) + +EXTRA_DIST = backupninja.in ninjahelper.in ninjareport.in + +edit = sed \ + -e "s,@CFGDIR\@,$(CFGDIR),g" \ + -e "s,@BASH\@,$(BASH),g" \ + -e "s,@AWK\@,$(AWK),g" \ + -e "s,@SED\@,$(SED),g" \ + -e 's,@datadir\@,$(pkgdatadir),g' \ + -e "s,@libdir\@,$(pkglibdir),g" \ + -e 's,@localstatedir\@,$(localstatedir),g' \ + -e 's,@prefix\@,$(prefix),g' + +#install-exec-hook: + +backupninja: $(srcdir)/backupninja.in + rm -f backupninja + $(edit) $(srcdir)/backupninja.in > backupninja + chmod ugo+x backupninja + +ninjahelper: $(srcdir)/ninjahelper.in + rm -f ninjahelper + $(edit) $(srcdir)/ninjahelper.in > ninjahelper + chmod ugo+x ninjahelper + +ninjareport: $(srcdir)/ninjareport.in + rm -f ninjareport + $(edit) $(srcdir)/ninjareport.in > ninjareport + chmod ugo+x ninjareport diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..f0ee2db --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,343 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" +sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT) +SCRIPTS = $(sbin_SCRIPTS) +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BASH = @BASH@ +CFGDIR = @CFGDIR@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +HAVE_RPM = @HAVE_RPM@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MKTEMP = @MKTEMP@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_cv_have_rpm = @ac_cv_have_rpm@ +am__leading_dot = @am__leading_dot@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# tyhle vygenerujeme ... +sbin_SCRIPTS = backupninja ninjahelper + +# a proto je taky musíme smazat ... +CLEANFILES = $(sbin_SCRIPTS) +EXTRA_DIST = backupninja.in ninjahelper.in ninjareport.in +edit = sed \ + -e "s,@CFGDIR\@,$(CFGDIR),g" \ + -e "s,@BASH\@,$(BASH),g" \ + -e "s,@AWK\@,$(AWK),g" \ + -e "s,@SED\@,$(SED),g" \ + -e 's,@datadir\@,$(pkgdatadir),g' \ + -e "s,@libdir\@,$(pkglibdir),g" \ + -e 's,@localstatedir\@,$(localstatedir),g' \ + -e 's,@prefix\@,$(prefix),g' + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-sbinSCRIPTS: $(sbin_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" + @list='$(sbin_SCRIPTS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f $$d$$p; then \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " $(sbinSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(sbindir)/$$f'"; \ + $(sbinSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(sbindir)/$$f"; \ + else :; fi; \ + done + +uninstall-sbinSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_SCRIPTS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \ + rm -f "$(DESTDIR)$(sbindir)/$$f"; \ + done +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(SCRIPTS) +installdirs: + for dir in "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: install-sbinSCRIPTS + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-sbinSCRIPTS + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-sbinSCRIPTS install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ + uninstall-am uninstall-sbinSCRIPTS + + +#install-exec-hook: + +backupninja: $(srcdir)/backupninja.in + rm -f backupninja + $(edit) $(srcdir)/backupninja.in > backupninja + chmod ugo+x backupninja + +ninjahelper: $(srcdir)/ninjahelper.in + rm -f ninjahelper + $(edit) $(srcdir)/ninjahelper.in > ninjahelper + chmod ugo+x ninjahelper + +ninjareport: $(srcdir)/ninjareport.in + rm -f ninjareport + $(edit) $(srcdir)/ninjareport.in > ninjareport + chmod ugo+x ninjareport +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/backupninja.in b/src/backupninja.in new file mode 100755 index 0000000..afb9556 --- /dev/null +++ b/src/backupninja.in @@ -0,0 +1,583 @@ +#!@BASH@ +# -*- mode: sh; sh-basic-offset: 3; indent-tabs-mode: nil; -*- +# +# |\_ +# B A C K U P N I N J A /()/ +# `\| +# +# Copyright (C) 2004-05 riseup.net -- property is theft. +# +# 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. +# + +##################################################### +## FUNCTIONS + +function setupcolors () { + BLUE="\033[34;01m" + GREEN="\033[32;01m" + YELLOW="\033[33;01m" + PURPLE="\033[35;01m" + RED="\033[31;01m" + OFF="\033[0m" + CYAN="\033[36;01m" + COLORS=($BLUE $GREEN $YELLOW $RED $PURPLE $CYAN) +} + +function colorize () { + if [ "$usecolors" == "yes" ]; then + local typestr=`echo "$@" | @SED@ 's/\(^[^:]*\).*$/\1/'` + [ "$typestr" == "Debug" ] && type=0 + [ "$typestr" == "Info" ] && type=1 + [ "$typestr" == "Warning" ] && type=2 + [ "$typestr" == "Error" ] && type=3 + [ "$typestr" == "Fatal" ] && type=4 + [ "$typestr" == "Halt" ] && type=5 + color=${COLORS[$type]} + endcolor=$OFF + echo -e "$color$@$endcolor" + else + echo -e "$@" + fi +} + +# We have the following message levels: +# 0 - debug - blue +# 1 - normal messages - green +# 2 - warnings - yellow +# 3 - errors - red +# 4 - fatal - purple +# 5 - halt - cyan +# First variable passed is the error level, all others are printed + +# if 1, echo out all warnings, errors, or fatal +# used to capture output from handlers +echo_debug_msg=0 + +usecolors=yes + +function printmsg() { + [ ${#@} -gt 1 ] || return + + type=$1 + shift + if [ $type == 100 ]; then + typestr=`echo "$@" | @SED@ 's/\(^[^:]*\).*$/\1/'` + [ "$typestr" == "Debug" ] && type=0 + [ "$typestr" == "Info" ] && type=1 + [ "$typestr" == "Warning" ] && type=2 + [ "$typestr" == "Error" ] && type=3 + [ "$typestr" == "Fatal" ] && type=4 + [ "$typestr" == "Halt" ] && type=5 + typestr="" + else + types=(Debug Info Warning Error Fatal Halt) + typestr="${types[$type]}: " + fi + + print=$[4-type] + + if [ $echo_debug_msg == 1 ]; then + echo -e "$typestr$@" >&2 + elif [ $debug ]; then + colorize "$typestr$@" >&2 + fi + + if [ $print -lt $loglevel ]; then + logmsg "$typestr$@" + fi +} + +function logmsg() { + if [ -w "$logfile" ]; then + echo -e `date "+%h %d %H:%M:%S"` "$@" >> $logfile + fi +} + +function passthru() { + printmsg 100 "$@" +} +function debug() { + printmsg 0 "$@" +} +function info() { + printmsg 1 "$@" +} +function warning() { + printmsg 2 "$@" +} +function error() { + printmsg 3 "$@" +} +function fatal() { + printmsg 4 "$@" + exit 2 +} +function halt() { + printmsg 5 "$@" + exit 2 +} + +msgcount=0 +function msg { + messages[$msgcount]=$1 + let "msgcount += 1" +} + +# +# enforces very strict permissions on configuration file $file. +# + +function check_perms() { + local file=$1 + debug "check_perms $file" + local perms + local owners + + perms=($(stat -L --format='%A' $file)) + debug "perms: $perms" + local gperm=${perms:4:3} + debug "gperm: $gperm" + local wperm=${perms:7:3} + debug "wperm: $wperm" + + owners=($(stat -L --format='%g %G %u %U' $file)) + local gid=${owners[0]} + local group=${owners[1]} + local owner=${owners[2]} + + if [ "$owner" != 0 ]; then + echo "Configuration files must be owned by root! Dying on file $file" + fatal "Configuration files must be owned by root! Dying on file $file" + fi + + if [ "$wperm" != '---' ]; then + echo "Configuration files must not be world writable/readable! Dying on file $file" + fatal "Configuration files must not be world writable/readable! Dying on file $file" + fi + + if [ "$gperm" != '---' ]; then + case "$admingroup" in + $gid|$group) :;; + + *) + if [ "$gid" != 0 ]; then + echo "Configuration files must not be writable/readable by group $group! Use the admingroup option in backupninja.conf. Dying on file $file" + fatal "Configuration files must not be writable/readable by group $group! Use the admingroup option in backupninja.conf. Dying on file $file" + fi + ;; + esac + fi +} + +# simple lowercase function +function tolower() { + echo "$1" | tr '[:upper:]' '[:lower:]' +} + +# simple to integer function +function toint() { + echo "$1" | tr -d '[:alpha:]' +} + +# +# function isnow(): returns 1 if the time/day passed as $1 matches +# the current time/day. +# +# format is <day> at <time>: +# sunday at 16 +# 8th at 01 +# everyday at 22 +# + +# we grab the current time once, since processing +# all the configs might take more than an hour. +nowtime=`date +%H` +nowday=`date +%d` +nowdayofweek=`date +%A` +nowdayofweek=`tolower "$nowdayofweek"` + +function isnow() { + local when="$1" + set -- $when + whendayofweek=$1; at=$2; whentime=$3; + whenday=`toint "$whendayofweek"` + whendayofweek=`tolower "$whendayofweek"` + whentime=`echo "$whentime" | @SED@ 's/:[0-9][0-9]$//' | @SED@ -r 's/^([0-9])$/0\1/'` + + if [ "$whendayofweek" == "everyday" -o "$whendayofweek" == "daily" ]; then + whendayofweek=$nowdayofweek + fi + + if [ "$whenday" == "" ]; then + if [ "$whendayofweek" != "$nowdayofweek" ]; then + whendayofweek=${whendayofweek%s} + if [ "$whendayofweek" != "$nowdayofweek" ]; then + return 0 + fi + fi + elif [ "$whenday" != "$nowday" ]; then + return 0 + fi + + [ "$at" == "at" ] || return 0 + [ "$whentime" == "$nowtime" ] || return 0 + + return 1 +} + +function usage() { + cat << EOF +$0 usage: +This script allows you to coordinate system backup by dropping a few +simple configuration files into @CFGDIR@/backup.d/. Typically, this +script is run hourly from cron. + +The following options are available: +-h, --help This usage message +-d, --debug Run in debug mode, where all log messages are + output to the current shell. +-f, --conffile FILE Use FILE for the main configuration instead + of @CFGDIR@/backupninja.conf +-t, --test Test run mode. This will test if the backup + could run, without actually preforming any + backups. For example, it will attempt to authenticate + or test that ssh keys are set correctly. +-n, --now Perform actions now, instead of when they might + be scheduled. No output will be created unless also + run with -d. + --run FILE Execute the specified action file and then exit. + Also puts backupninja in debug mode. + +When in debug mode, output to the console will be colored: +EOF + usecolors=yes + colorize "Debug: Debugging info (when run with -d)" + colorize "Info: Informational messages (verbosity level 4)" + colorize "Warning: Warnings (verbosity level 3 and up)" + colorize "Error: Errors (verbosity level 2 and up)" + colorize "Fatal: Errors which halt a given backup action (always shown)" + colorize "Halt: Errors which halt the whole backupninja run (always shown)" +} + +## +## this function handles the running of a backup action +## +## these globals are modified: +## halts, fatals, errors, warnings, actions_run, errormsg +## + +function process_action() { + local file="$1" + local suffix="$2" + local run="no" + setfile $file + + # skip over this config if "when" option + # is not set to the current time. + getconf when "$defaultwhen" + if [ "$processnow" == 1 ]; then + info ">>>> starting action $file (because of --now)" + run="yes" + elif [ "$when" == "hourly" ]; then + info ">>>> starting action $file (because 'when = hourly')" + run="yes" + else + IFS=$'\t\n' + for w in $when; do + IFS=$' \t\n' + isnow "$w" + ret=$? + IFS=$'\t\n' + if [ $ret == 0 ]; then + debug "skipping $file because it is not $w" + else + info ">>>> starting action $file (because it is $w)" + run="yes" + fi + done + IFS=$' \t\n' + fi + debug $run + [ "$run" == "no" ] && return + + let "actions_run += 1" + + # call the handler: + local bufferfile=`maketemp backupninja.buffer` + echo "" > $bufferfile + echo_debug_msg=1 + ( + . $scriptdirectory/$suffix $file + ) 2>&1 | ( + while read a; do + echo $a >> $bufferfile + [ $debug ] && colorize "$a" + done + ) + retcode=$? + # ^^^^^^^^ we have a problem! we can't grab the return code "$?". grrr. + echo_debug_msg=0 + + _warnings=`cat $bufferfile | grep "^Warning: " | wc -l` + _errors=`cat $bufferfile | grep "^Error: " | wc -l` + _fatals=`cat $bufferfile | grep "^Fatal: " | wc -l` + _halts=`cat $bufferfile | grep "^Halt: " | wc -l` + + ret=`grep "\(^Warning: \|^Error: \|^Fatal: \|Halt: \)" $bufferfile` + rm $bufferfile + if [ $_halts != 0 ]; then + msg "*halt* -- $file" + errormsg="$errormsg\n== halt request from $file==\n\n$ret\n" + passthru "Halt: <<<< finished action $file: FAILED" + elif [ $_fatals != 0 ]; then + msg "*failed* -- $file" + errormsg="$errormsg\n== fatal errors from $file ==\n\n$ret\n" + passthru "Fatal: <<<< finished action $file: FAILED" + elif [ $_errors != 0 ]; then + msg "*error* -- $file" + errormsg="$errormsg\n== errors from $file ==\n\n$ret\n" + error "<<<< finished action $file: ERROR" + elif [ $_warnings != 0 ]; then + msg "*warning* -- $file" + errormsg="$errormsg\n== warnings from $file ==\n\n$ret\n" + warning "<<<< finished action $file: WARNING" + else + msg "success -- $file" + info "<<<< finished action $file: SUCCESS" + fi + + let "halts += _halts" + let "fatals += _fatals" + let "errors += _errors" + let "warnings += _warnings" +} + +##################################################### +## MAIN + +setupcolors +conffile="@CFGDIR@/backupninja.conf" +loglevel=3 + +## process command line options + +while [ $# -ge 1 ]; do + case $1 in + -h|--help) usage;; + -d|--debug) debug=1;; + -t|--test) test=1;debug=1;; + -n|--now) processnow=1;; + -f|--conffile) + if [ -f $2 ]; then + conffile=$2 + else + echo "-f|--conffile option must be followed by an existing filename" + fatal "-f|--conffile option must be followed by an existing filename" + usage + fi + # we shift here to avoid processing the file path + shift + ;; + --run) + debug=1 + if [ -f $2 ]; then + singlerun=$2 + processnow=1 + else + echo "--run option must be followed by a backupninja action file" + fatal "--run option must be followed by a backupninja action file" + usage + fi + shift + ;; + *) + debug=1 + echo "Unknown option $1" + fatal "Unknown option $1" + usage + exit + ;; + esac + shift +done + +#if [ $debug ]; then +# usercolors=yes +#fi + +## Load and confirm basic configuration values + +# bootstrap +if [ ! -r "$conffile" ]; then + echo "Configuration file $conffile not found." + fatal "Configuration file $conffile not found." +fi + +# find $libdirectory +libdirectory=`grep '^libdirectory' $conffile | @AWK@ '{print $3}'` +if [ -z "$libdirectory" ]; then + if [ -d "@libdir@" ]; then + libdirectory="@libdir@" + else + echo "Could not find entry 'libdirectory' in $conffile." + fatal "Could not find entry 'libdirectory' in $conffile." + fi +else + if [ ! -d "$libdirectory" ]; then + echo "Lib directory $libdirectory not found." + fatal "Lib directory $libdirectory not found." + fi +fi + +# include shared functions +. $libdirectory/tools +. $libdirectory/vserver + +setfile $conffile + +# get global config options (second param is the default) +getconf configdirectory @CFGDIR@/backup.d +getconf scriptdirectory @datadir@ +getconf reportdirectory +getconf reportemail +getconf reporthost +getconf reportspace +getconf reportsuccess yes +getconf reportuser +getconf reportwarning yes +getconf loglevel 3 +getconf when "Everyday at 01:00" +defaultwhen=$when +getconf logfile @localstatedir@/log/backupninja.log +getconf usecolors "yes" +getconf SLAPCAT /usr/sbin/slapcat +getconf LDAPSEARCH /usr/bin/ldapsearch +getconf RDIFFBACKUP /usr/bin/rdiff-backup +getconf CSTREAM /usr/bin/cstream +getconf MYSQLADMIN /usr/bin/mysqladmin +getconf MYSQL /usr/bin/mysql +getconf MYSQLHOTCOPY /usr/bin/mysqlhotcopy +getconf MYSQLDUMP /usr/bin/mysqldump +getconf PGSQLDUMP /usr/bin/pg_dump +getconf PGSQLDUMPALL /usr/bin/pg_dumpall +getconf PGSQLUSER postgres +getconf GZIP /bin/gzip +getconf RSYNC /usr/bin/rsync +getconf admingroup root + +# initialize vservers support +# (get config variables and check real vservers availability) +init_vservers nodialog + +if [ ! -d "$configdirectory" ]; then + echo "Configuration directory '$configdirectory' not found." + fatal "Configuration directory '$configdirectory' not found." +fi + +[ -f "$logfile" ] || touch $logfile + +if [ "$UID" != "0" ]; then + echo "`basename $0` can only be run as root" + exit 1 +fi + +## Process each configuration file + +# by default, don't make files which are world or group readable. +umask 077 + +# these globals are set by process_action() +halts=0 +fatals=0 +errors=0 +warnings=0 +actions_run=0 +errormsg="" + +if [ "$singlerun" ]; then + files=$singlerun +else + files=`find $configdirectory -follow -mindepth 1 -maxdepth 1 -type f ! -name '.*.swp' | sort -n` + + if [ -z "$files" ]; then + fatal "No backup actions configured in '$configdirectory', run ninjahelper!" + fi +fi + +for file in $files; do + [ -f "$file" ] || continue + [ "$halts" = "0" ] || continue + + check_perms ${file%/*} # check containing dir + check_perms $file + suffix="${file##*.}" + base=`basename $file` + if [ "${base:0:1}" == "0" -o "$suffix" == "disabled" ]; then + info "Skipping $file" + continue + fi + + if [ -e "$scriptdirectory/$suffix" ]; then + process_action $file $suffix + else + error "Can't process file '$file': no handler script for suffix '$suffix'" + msg "*missing handler* -- $file" + fi +done + +## mail the messages to the report address + +if [ $actions_run == 0 ]; then doit=0 +elif [ "$reportemail" == "" ]; then doit=0 +elif [ $fatals != 0 ]; then doit=1 +elif [ $errors != 0 ]; then doit=1 +elif [ "$reportsuccess" == "yes" ]; then doit=1 +elif [ "$reportwarning" == "yes" -a $warnings != 0 ]; then doit=1 +else doit=0 +fi + +if [ $doit == 1 ]; then + debug "send report to $reportemail" + hostname=`hostname` + [ $warnings == 0 ] || subject="WARNING" + [ $errors == 0 ] || subject="ERROR" + [ $fatals == 0 ] || subject="FAILED" + + { + for ((i=0; i < ${#messages[@]} ; i++)); do + echo ${messages[$i]} + done + echo -e "$errormsg" + if [ "$reportspace" == "yes" ]; then + previous="" + for i in $(ls "$configdirectory"); do + backuploc=$(grep ^directory "$configdirectory"/"$i" | @AWK@ '{print $3}') + if [ "$backuploc" != "$previous" -a -n "$backuploc" ]; then + df -h "$backuploc" + previous="$backuploc" + fi + done + fi + } | mail -s "backupninja: $hostname $subject" $reportemail +fi + +if [ $actions_run != 0 ]; then + info "FINISHED: $actions_run actions run. $fatals fatal. $errors error. $warnings warning." + if [ "$halts" != "0" ]; then + info "Backup was halted prematurely. Some actions may not have run." + fi +fi + +if [ -n "$reporthost" ]; then + debug "send $logfile to $reportuser@$reporthost:$reportdirectory" + rsync -qt $logfile $reportuser@$reporthost:$reportdirectory +fi diff --git a/src/ninjahelper.in b/src/ninjahelper.in new file mode 100755 index 0000000..040727b --- /dev/null +++ b/src/ninjahelper.in @@ -0,0 +1,268 @@ +#!@BASH@ +# -*- mode: sh; sh-basic-offset: 3; indent-tabs-mode: nil; -*- + +#################################################### +## Functions + +## +## returns the next available file name given a file +## in the form @CFGDIR@/backup.d/10.sys +## sets variable $next_filename +## +get_next_filename() { + next_filename=$1 + dir=`dirname $next_filename` + file=`basename $next_filename` + number=${file:0:2} + suffix=${file:3} + while [ -f $next_filename ]; do + let "number += 1" + next_filename="$dir/$number.$suffix" + done +} + +## +## installs packages (passed in as $@) if not present +## +require_packages() { + for pkg in "$@"; do + installed=`dpkg -s $pkg | grep 'ok installed'` + if [ -z "$installed" ]; then + booleanBox "install $pkg?" "This backup action requires package $pkg. Do you want to install it now?" + if [ $? = 0 ]; then + apt-get install $pkg + echo "hit return to continue...." + read + fi + fi + done +} + +## +## menu for the wizards +## +donew() { + # (re-)initialize vservers support + init_vservers + # menu + listBegin "new action menu" "select an action to create" + listItem return "return to main menu" + for data in $HELPERS; do + data=${data//_/ } + helper_function=${data%%:*} + helper_info=${data##*:} + listItem $helper_function "$helper_info" + done + listDisplay menu + + [ $? = 1 ] && return + result="$REPLY" + [ "$result" = "return" -o "$result" = "" ] && return + run_wizard=${result}_wizard + $run_wizard + result=$? + # 0 is ok, 1 is cancel, anything else is bad. + if [ $result != 1 -a $result != 0 ]; then + echo "An error occurred ($result), bailing out. Hit return to continue." + read + fi +} + +do_rm_action() { + booleanBox "remove action" "Are you sure you want to remove action file $1?" + if [ $? = 0 ]; then + rm $1; + fi +} + +do_run() { + backupninja --run $1 + echo "Hit return to continue..." + read +} + +do_xedit() { + if [ -z "$EDITOR" -o ! -x "`which $EDITOR`" ]; then + if [ -h /etc/alternatives/editor -a -x "`readlink /etc/alternatives/editor`" ]; then + EDITOR="`readlink /etc/alternatives/editor`" + elif [ -x "`which nano`" ]; then + EDITOR="`which nano`" + elif [ -x "`which vim`" ]; then + EDITOR="`which vim`" + elif [ -x "`which vi`" ]; then + EDITOR="`which vi`" + else + echo "No suitable editor found." + echo "Please define $EDITOR or configure /etc/alternatives/editor." + exit + fi + fi + $EDITOR $1 +} + +do_run_test() { + backupninja --test --run $1 + echo "Hit return to continue..." + read +} + +do_disable() { + mv $1 $1.disabled +} + +do_enable() { + mv $1 ${1%.*} +} + +do_rename() { + dir=`dirname $1` + filename=`basename $1` + inputBox "rename action" "enter a new filename" $filename + mv $dir/$filename $dir/$REPLY +} + +doaction() { + action=$1 + base=`basename $action` + if [ "${base##*.}" == "disabled" ]; then + enable="enable"; + else + enable="disable"; + fi + while true; do + menuBox "action menu" "$action $first" \ + main "return to main menu" \ + view "view configuration" \ + xedit "launch external editor" \ + $enable "$enable action" \ + name "change the filename" \ + run "run this action now" \ + test "do a test run" \ + kill "remove this action" + [ $? = 1 ] && return; + result="$REPLY" + case "$result" in + "view") dialog --textbox $action 0 0;; + "xedit") do_xedit $action;; + "disable") do_disable $action; return;; + "enable") do_enable $action; return;; + "name") do_rename $action; return;; + "run") do_run $action;; + "test") do_run_test $action;; + "kill") do_rm_action $action; return;; + "main") return;; + esac + done +} + +##################################################### +## begin program + +if [ ! -x "`which dialog`" ]; then + echo "ninjahelper is a menu based wizard for backupninja." + echo "It requires 'dialog' in order to run. Do you want to install dialog now?" + while true; do + echo -n "(yes/no): " + read install + if [ "$install" == "yes" ]; then + apt-get install dialog + break + elif [ "$install" == "no" ]; then + exit + else + echo "You must answer 'yes' or 'no'" + fi + done +fi + +# bootstrap +conffile="@CFGDIR@/backupninja.conf" +if [ ! -r "$conffile" ]; then + echo "Configuration file $conffile not found." + exit 1 +fi + +# find $libdirectory +libdirectory=`grep '^libdirectory' $conffile | @AWK@ '{print $3}'` +if [ -z "$libdirectory" ]; then + if [ -d "@libdir@" ]; then + libdirectory="@libdir@" + else + echo "Could not find entry 'libdirectory' in $conffile." + exit 1 + fi +else + if [ ! -d "$libdirectory" ]; then + echo "Lib directory $libdirectory not found." + exit 1 + fi +fi + +# include shared functions +. $libdirectory/easydialog +. $libdirectory/tools +. $libdirectory/vserver + +# am I running as root? +if [ "$UID" != "0" ]; then + msgBox "warning" "`basename $0` must be run by root!" + exit 1 +fi + +# get global config options (second param is the default) +setfile $conffile +getconf configdirectory @CFGDIR@/backup.d +if [ ! -d $configdirectory ]; then + msgBox "warning" "The backupninja configuration directory $configdirectory does not exist. Ninjahelper cannot run without it!" + exit 1 +fi +getconf scriptdirectory @datadir@ + +# load all the helpers +HELPERS="" +for file in `find $scriptdirectory -follow -name '*.helper'`; do + . $file + if [ $? != 0 ]; then + echo "An error occurred while loading $file. Hit return to continue." + read + fi +done + +setApplicationTitle "ninjahelper" +setDimension 75 19 + +##################################################### +## main event loop + +while true; do + +menulist= +action= +let "i = 1" +for file in `find ${configdirectory} -follow -mindepth 1 -maxdepth 1 -type f ! -name '.*.swp' | sort -n`; do + menulist="$menulist $i $file" + actions[$i]=$file + let "i += 1" +done + +menuBox "main menu" "Select a backup action for more options, or create a new action:" $menulist \ + new "create a new backup action" \ + quit "leave ninjahelper" + +[ $? = 1 -o $? = 255 ] && exit 0; + +choice="$REPLY" +if [ "$choice" == "new" ]; then + donew; +elif [ "$choice" == "quit" ]; then + exit 0; +else + action=${actions[$choice]}; + if [ -f "$action" ]; then + doaction $action + else + msgBox "error" "error: cannot find the file '$action'" + fi +fi + +done diff --git a/src/ninjareport.in b/src/ninjareport.in new file mode 100755 index 0000000..b68efb8 --- /dev/null +++ b/src/ninjareport.in @@ -0,0 +1,205 @@ +#!@BASH@ +# -*- mode: sh; sh-basic-offset: 3; indent-tabs-mode: nil; -*- +# +# +# Ninjareport - generate a single simple report for a lot of hosts +# +# requires logtail +# +# Copyright (C) 2007 - riseup.net -- property is theft. + +# TODO: +# +# . check for logs that are never updating and warn +# . change constantly updating logs (maildir) to be more friendly +# . documentation +# . maybe make config option that lists all hosts that should report-in, and if +# one doesn't then warn +# . restrict rsync somehow? +# . abstract path for logtail +# . on the report master, the reportdirectory should be set where the reports will be going, note this + +mail=1 +display=0 + +process() { + +# look in the logfile for any lines like the following: +# Jan 20 01:02:46 Info: FINISHED: 2 actions run. 0 fatal. 0 error. 0 warning. +# +# note: some backups never finish, such as the maildir one, need to handle these +# perhaps by looking for FAILED messages? +# note2: what about logs that aren't being updated? this is a failure case and +# should be looked for +# note3: there are also these entries: +# Jan 20 14:00:01 Fatal: No backup actions configured in '/etc/backup.d', run ninjahelper! + +# The following has to be done without invoking a subshell (see BashFAQ #24) +logupdates=`maketemp ninjadata` + +/usr/sbin/logtail -f $host > $logupdates +grep FINISHED $logupdates | +( + fatal=0 + warning=0 + error=0 + + while read line + do + line_array=($line) + fatal=$(($fatal + ${line_array[8]})) + error=$(($error + ${line_array[10]})) + warning=$(($warning + ${line_array[12]})) + done + if (( $fatal || $warning || $error )); then + echo "`basename $host .log`: $fatal fatals found, $error errors found, $warning warnings found" >> $statusfile + echo "" >> $reportappend + echo "`basename $host .log` log entries since last ninjareport" >> $reportappend + echo "---------" >> $reportappend + cat $logupdates >> $reportappend + rm $logupdates + fi +) + +} + +generatereport() { + +reportfile=`maketemp ninjareport` + +# Generate a report, only if there are failures +if [ -s $statusfile ]; then + echo " backupninja mission failures - `date`" >> $reportfile + echo " --------------------------------------------------------------" >> $reportfile + echo "" >> $reportfile + cat $statusfile | column -t >> $reportfile + echo "" >> $reportfile + echo " log entries from failed reports" >> $reportfile + echo " -----------------------------------" >> $reportfile + cat $reportappend >> $reportfile +fi + +} + +usage() { + cat << EOF +This script generates a backupninja status report for all configured +systems. It requires that each status report is placed in a spot where +ninjareport can read it, reports are mailed to the reportemail +configured in @CFGDIR@/backupninja.conf. + +The following options are available: +-h, --help This usage message +-f, --conffile FILE Use FILE for the configuration instead + of @CFGDIR@/backupninja.conf +-m, --mail <email> Mail the report to this address +-o, --out Don't mail the report, just display it + +EOF +} + +##################################################### +## MAIN + +conffile="@CFGDIR@/backupninja.conf" + +## process command line options + +while [ $# -ge 1 ]; do + case $1 in + -h|--help) + usage + exit 0 + ;; + -f|--conffile) + if [ -f $2 ]; then + conffile=$2 + else + echo "-f|--conffile option must be followed by an existing filename" + fatal "-f|--conffile option must be followed by an existing filename" + usage + fi + # we shift here to avoid processing the file path + shift + ;; + -m|--mail) + reportemail=$2 + shift + ;; + -o|--out) + mail=0 + display=1 + ;; + *) + echo "Unknown option $1" + usage + exit + ;; + esac + shift +done + +## Load and confirm basic configuration values + +# bootstrap +if [ ! -r "$conffile" ]; then + echo "Configuration file $conffile not found." + fatal "Configuration file $conffile not found." +fi + +# find $libdirectory +libdirectory=`grep '^libdirectory' $conffile | awk '{print $3}'` +if [ -z "$libdirectory" ]; then + if [ -d "@libdir@" ]; then + libdirectory="@libdir@" + else + echo "Could not find entry 'libdirectory' in $conffile." + exit 1 + fi +else + if [ ! -d "$libdirectory" ]; then + echo "Lib directory $libdirectory not found." + exit 1 + fi +fi + +# include shared functions +. $libdirectory/tools + +setfile $conffile + +getconf reportdirectory +getconf reportemail + +## Process each configuration file + +hosts=`find $reportdirectory -follow -mindepth 1 -maxdepth 1 -type f ! -name '*.offset' | sort -n` + +if [ -z "$hosts" ]; then + echo "Fatal: No backupninja reports found in '$reportdirectory'!" + mail=0 +fi + +statusfile=`maketemp ninjastatus` +reportappend=`maketemp ninjaappend` + +for host in $hosts; do + [ -f "$host" ] || continue + # Check somehow that the file is a valid report file + process $host +done + +generatereport + +## mail the report to the report address or display it + +if [ -s $reportfile ]; then + if [ $mail == 1 ]; then + mail -s "backupninja mission failure report" $reportemail < $reportfile + fi +fi + +if [ $display == 1 ]; then + cat $reportfile +fi + |