aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElijah Saxon <elijah@riseup.net>2005-07-04 06:39:00 +0000
committerElijah Saxon <elijah@riseup.net>2005-07-04 06:39:00 +0000
commit4c8e2839949be4603fbb8fb9e7a7e536e59c1dc4 (patch)
tree97f709c75eb39f15a7c29dfcfe11cb0ac0a15853
parent2046272df5e3efeb8c6174b77c807ed40a2e3bd1 (diff)
downloadbackupninja-4c8e2839949be4603fbb8fb9e7a7e536e59c1dc4.tar.gz
backupninja-4c8e2839949be4603fbb8fb9e7a7e536e59c1dc4.tar.bz2
created ninjahelper
-rw-r--r--handlers/easydialog.sh182
-rw-r--r--handlers/ldap.helper75
-rw-r--r--handlers/mysql.helper109
-rw-r--r--handlers/rdiff.helper69
-rw-r--r--handlers/sys.helper31
-rwxr-xr-xninjahelper221
6 files changed, 687 insertions, 0 deletions
diff --git a/handlers/easydialog.sh b/handlers/easydialog.sh
new file mode 100644
index 0000000..ce7d31e
--- /dev/null
+++ b/handlers/easydialog.sh
@@ -0,0 +1,182 @@
+#!/bin/bash
+
+# copyright 2002 lmoore@tump.com under the terms of the GNU LGPL.
+
+# whiptail has trouble being called in the foo=$(whiptail ...) fashion for
+# some reason. this is very annoying. this means that we need to use
+# temporary files to store the answers from the input and list based boxes
+# and then read the answers into a REPLY variable. that just really
+# stinks, oh well, that's what you get when you have a weak link
+# implementation...
+#
+# inputBox and passwordBox could be refactored to use a common function
+
+test -z "$WIDTH" && WIDTH=0
+test -z "$HEIGHT" && HEIGHT=0
+BACKTITLE=""
+DIALOG=dialog
+HELP=
+
+setApplicationTitle() {
+ BACKTITLE=$*
+}
+
+setHelp() {
+ HELP="$@"
+}
+
+setDimension() {
+ WIDTH=$1
+ HEIGHT=$2
+}
+
+booleanBox() {
+ $DIALOG --backtitle "$BACKTITLE" --title "$1" \
+ --yesno "$2" $HEIGHT $WIDTH
+}
+
+msgBox() {
+ $DIALOG --backtitle "$BACKTITLE" --title "$1" \
+ --msgbox "$2" $HEIGHT $WIDTH
+}
+
+gaugeBox() {
+ $DIALOG --backtitle "$BACKTITLE" --title "$1" \
+ --gauge "$2" $HEIGHT $WIDTH 0
+}
+
+inputBox() {
+ local temp=$(mktemp -t) || exit 1
+ trap "rm -f $temp" 0
+ REPLY=
+ $DIALOG --backtitle "$BACKTITLE" --title "$1" \
+ --inputbox "$2" $HEIGHT $WIDTH "$3" 2> $temp
+ local status=$?
+ [ $status = 0 ] && REPLY=$(cat $temp)
+ rm -f $temp
+ return $status
+}
+
+# Xdialog and {dialog,whiptail} use different mechanism to "qoute" the
+# values from a checklist. {dialog,whiptail} uses standard double quoting
+# while Xdialog uses a "/" as the separator. the slash is arguably better,
+# but the double quoting is more standard. anyway, this function can be
+# overridden to allow a derived implementation to change it's quoting
+# mechanism to the standard double-quoting one. it receives two
+# arguements, the file that has the data and the box type.
+_listReplyHook() {
+ cat $1
+}
+
+# this is the base implementation of all the list based boxes, it works
+# out nicely that way. the real function just passes it's arguments to
+# this function with an extra argument specifying the actual box that
+# needs to be rendered.
+_genericListBox() {
+ local box=$1
+ shift 1
+ local title=$1
+ local text=$2
+ shift 2
+ local temp=$(mktemp -t) || exit 1
+ trap "rm -f $temp" 0
+ REPLY=
+ $DIALOG $HELP --backtitle "$BACKTITLE" --title "$title" \
+ $box "$text" $HEIGHT $WIDTH 10 \
+ "$@" 2> $temp
+ local status=$?
+ [ $status = 0 ] && REPLY=$(_listReplyHook $temp $box)
+ rm -f $temp
+ return $status
+}
+
+menuBox() {
+ _genericListBox --menu "$@"
+}
+
+menuBoxHelp() {
+ HELP="--item-help"
+ _genericListBox --menu "$@"
+ status=$?
+ HELP=
+ return $status
+}
+
+menuBoxHelpFile() {
+ HELP="--help-button"
+ _genericListBox --menu "$@"
+ status=$?
+ HELP=
+ return $status
+}
+
+
+checkBox() {
+ _genericListBox --checklist "$@"
+}
+
+radioBox() {
+ _genericListBox --radiolist "$@"
+}
+
+textBox() {
+ $DIALOG --backtitle "$BACKTITLE" --title "$1" --textbox "$2" $HEIGHT $WIDTH
+}
+
+passwordBox() {
+ local temp=$(mktemp -t) || exit 1
+ trap "rm -f $temp" 0
+ REPLY=
+ $DIALOG --backtitle "$BACKTITLE" --title "$1" \
+ --passwordbox "$2" $HEIGHT $WIDTH 2> $temp
+ local status=$?
+ [ $status = 0 ] && REPLY=$(cat $temp)
+ rm -f $temp
+ return $status
+}
+
+_form_gap=2
+startForm() {
+ _form_title=$1
+ _form_items=0
+ _form_labels=
+ _form_text=
+}
+
+formItem() {
+ _form_labels[$_form_items]=$1
+ _form_text[$_form_items]=$2
+ let "_form_items += 1"
+}
+
+displayForm() {
+ local temp=$(mktemp -t) || exit 1
+
+ max_length=0
+ for ((i=0; i < ${#_form_labels[@]} ; i++)); do
+ label=${_form_labels[$i]}
+ length=`expr length $label`
+ if [ $length -gt $max_length ]; then
+ max_length=$length
+ fi
+ done
+ let "max_length += 2"
+
+ local form=
+ local xpos=1
+ for ((i=0; i < $_form_items ; i++)); do
+ label=${_form_labels[$i]}
+ text=${_form_text[$i]}
+ if [ "$text" == "" ]; then
+ text='_empty_'
+ fi
+ form=`echo -e "$form $label $xpos 1" $text "$xpos $max_length 30 30"`
+ let "xpos += _form_gap"
+ done
+
+ $DIALOG --form "$_form_title" 0 0 20 $form 2> $temp
+ local status=$?
+ [ $status = 0 ] && REPLY=$(cat $temp)
+ rm -f $temp
+ return $status
+}
diff --git a/handlers/ldap.helper b/handlers/ldap.helper
new file mode 100644
index 0000000..44b08cb
--- /dev/null
+++ b/handlers/ldap.helper
@@ -0,0 +1,75 @@
+
+ldap_create_file() {
+while true; do
+ checkBox "ldap action wizard" "check options (slapcat OR ldapsearch)" \
+ "slapcat" "export ldif using slapcat" on \
+ "ldapsearch" "export ldif using ldapsearch" off \
+ "compress" "compress the ldif output files" on
+ status=$?
+ compress="compress = off"
+ method="method = <unset>"
+ restart="restart = no"
+ binddn=""
+ passwordfile=""
+ [ $status = 1 ] && return;
+ result="$REPLY"
+ for opt in $result; do
+ case $opt in
+ '"compress"') compress="compress = on";;
+ '"slapcat"')
+ method="method = slapcat"
+ [ "$_RESTART" == "yes" ] && restart="restart = yes"
+ ;;
+ '"ldapsearch"')
+ method="method = ldapsearch"
+ inputBox "ldap action wizard" "ldapsearch requires authentication. Specify here what password file to use. It must have the password with no trailing return and it should not be world readable."
+ [ $? = 1 ] && return
+ passwordfile="passwordfile = $REPLY"
+ inputBox "ldap action wizard" "ldapsearch requires authentication. Specify here what DN to bind as:"
+ [ $? = 1 ] && return
+ binddn="binddn = $REPLY"
+ require_packages ldap-utils
+ ;;
+ esac
+ done
+ get_next_filename $configdirectory/30.ldap
+ cat > $next_filename <<EOF
+$method
+$compress
+$restart
+$binddn
+$passwordfile
+# backupdir = /var/backups/ldap
+# conf = /etc/ldap/slapd.conf
+# databases = all
+EOF
+ chmod 000 $next_filename
+ return
+done
+}
+
+ldap_wizard() {
+ bdb=no
+ ldbm=no
+ for backend in `grep -e "^backend" /etc/ldap/slapd.conf | awk '{print $2}'`; do
+ if [ "$backend" == "bdb" -a "$bdb" == "no" ]; then
+ bdb=yes
+ elif [ "$backend" == "ldbm" -a "$ldbm" == "no" ]; then
+ ldbm=yes
+ fi
+ done
+
+ if [ "$bdb" == "yes" -a "$ldbm" == "no" ]; then
+ msgBox "ldap action wizard" "It looks like the backend in your slapd.conf is set to BDB. If this is not the case, exit this wizard! From this point on, we will assume BDB backend, which might have disasterious consequences if this is incorrect."
+ _RESTART=no
+ ldap_create_file
+ elif [ "$ldbm" == "yes" ]; then
+ msgBox "ldap action wizard" "It looks like the backend in your slapd.conf is set to LDBM. Because of this, you will have less options (because it is not safe to use slapcat while slapd is running LDBM)."
+ _RESTART=yes
+ ldap_create_file
+ else
+ msgBox "ldap action wizard" "I couldn't find any backends in your slapd.conf. Bailing out."
+ return
+ fi
+}
+
diff --git a/handlers/mysql.helper b/handlers/mysql.helper
new file mode 100644
index 0000000..765f228
--- /dev/null
+++ b/handlers/mysql.helper
@@ -0,0 +1,109 @@
+
+do_mysql_user() {
+ inputBox "mysql action wizard" "specify a system user:"
+ do_mysql_final "user = $REPLY"
+}
+
+do_mysql_password() {
+ inputBox "mysql action wizard" "specify a mysql user:"
+ user=$REPLY
+ inputBox "mysql action wizard" "specify the mysql user's password:"
+ password=$REPLY
+ do_mysql_final "dbusername = $user\ndbpassword = $password"
+}
+
+do_mysql_debian() {
+ _DISABLE_HOTCOPY=yes
+ do_mysql_final "configfile = /etc/mysql/debian.cnf"
+}
+
+do_mysql_user() {
+ inputBox "mysql action wizard" "what system user does mysql backup use?"
+ do_mysql_final "user = $REPLY"
+}
+
+do_mysql_final() {
+ if [ -z "$_DISABLE_HOTCOPY" ]; then
+ checkBox "mysql action wizard" "check options" \
+ "sqldump" "create a backup using mysqldump (more compat)." off \
+ "hotcopy" "create a backup using mysqlhotcopy (faster)." on \
+ "compress" "compress the sql output files" on
+ status=$?
+ sqldump="sqldump = off"
+ hotcopy="hotcopy = off"
+ else
+ checkBox "mysql action wizard" "check options" \
+ "compress" "compress the sql output files" on
+ status=$?
+ sqldump="sqldump = on"
+ hotcopy="hotcopy = off"
+ fi
+
+ [ $status = 1 ] && return;
+ result="$REPLY"
+ compress="compress = off"
+ for opt in $result; do
+ case $opt in
+ '"sqldump"') sqldump="sqldump = on";;
+ '"hotcopy"') hotcopy="hotcopy = on";;
+ '"compress"') compress="compress = on";;
+ esac
+ done
+ get_next_filename $configdirectory/20.mysql
+ echo -e $@ > $next_filename
+ cat >> $next_filename <<EOF
+$sqldump
+$hotcopy
+$compress
+# databases = all
+# backupdir = /var/backups/mysql
+# dbhost = localhost
+EOF
+ chmod 000 $next_filename
+}
+
+mysql_wizard() {
+ while true; do
+ _DISABLE_HOTCOPY=
+ menuBoxHelpFile "mysql action wizard" "choose a mysql authentication method:" \
+ user "change to a linux user first." \
+ password "manually specify mysql user and password." \
+ debian "use default mysql user debian-sys-maint."
+ status=$?
+ if [ $status = 2 ]; then
+ # show help.
+ helptmp="/tmp/backupninja.help.$$"
+ cat > $helptmp <<EOF
+To connect to mysql, backupninja must authenticate.
+There are three possible authentication methods:
+
+USER
+With this method, you specify a system user. Backupninja will
+then become this user before running mysqldump or mysqlhotcopy.
+The result is that ~/.my.cnf is used for authentication.
+
+PASSWORD
+With this method, you manually specify a mysql user and
+password in the backup action configuration.
+
+DEBIAN
+With this method, we use the debian-sys-maint user which is
+already defined in /etc/mysql/debian.cnf. If you are running
+debian, this is recommended, because no further configuration
+is needed. The drawback is that this is incompatible with
+mysqlhotcopy: you must use mysqldump.
+EOF
+ dialog --textbox $helptmp 0 0
+ rm $helptmp
+ fi
+
+ [ $status = 1 ] && return;
+ result="$REPLY"
+ case "$result" in
+ "user") do_mysql_user;return;;
+ "password") do_mysql_password;return;;
+ "debian") do_mysql_debian;return;;
+ esac
+ done
+}
+
diff --git a/handlers/rdiff.helper b/handlers/rdiff.helper
new file mode 100644
index 0000000..1364367
--- /dev/null
+++ b/handlers/rdiff.helper
@@ -0,0 +1,69 @@
+
+rdiff_wizard() {
+ require_packages rdiff-backup
+ startForm "rdiff action wizard"
+ formItem "keep" "60D"
+ formItem "dest_directory" "/backups/mybackup"
+ formItem "dest_host" "backuphost"
+ formItem "dest_user" "backupuser"
+ displayForm
+
+ [ $? = 1 ] && return;
+
+ set -- $REPLY
+ keep=$1
+ directory=$2
+ host=$3
+ user=$4
+
+ startForm "rdiff action wizard: includes"
+ formItem include /var/spool/cron/crontabs
+ formItem include /var/backups
+ formItem include /etc
+ formItem include /root
+ formItem include /home
+ formItem include /usr/local/__star__bin
+ formItem include /var/lib/dpkg/status__star__
+ formItem include
+ formItem include
+ formItem include
+ displayForm
+
+ [ $? = 1 ] && return;
+
+ includes=
+ for i in $REPLY; do
+ [ "$i" != "_empty_" ] && includes="$includes\ninclude = $i"
+ done
+
+ startForm "rdiff action wizard: excludes"
+ formItem exclude /home/__star__/.gnupg
+ formItem exclude
+ formItem exclude
+ displayForm
+
+ [ $? = 1 ] && return;
+
+ excludes=
+ for i in $REPLY; do
+ [ "$i" != "_empty_" ] && excludes="$excludes\nexclude = $i"
+ done
+
+ get_next_filename $configdirectory/90.rdiff
+ cat > $next_filename <<EOF
+[source]
+type = local
+keep = $keep
+EOF
+ echo -e $includes >> $next_filename
+ echo -e $excludes >> $next_filename
+ cat >> $next_filename <<EOF
+
+[dest]
+type = remote
+directory = $directory
+host = $host
+user = $user
+EOF
+ chmod 000 $next_filename
+}
diff --git a/handlers/sys.helper b/handlers/sys.helper
new file mode 100644
index 0000000..22eb01d
--- /dev/null
+++ b/handlers/sys.helper
@@ -0,0 +1,31 @@
+
+sys_wizard() {
+ require_packages hwinfo
+ checkBox "new sys action" "check options" \
+ "packages" "list of all installed packages." on \
+ "partitions" "the partition table of all disks." on \
+ "hardware" "detailed hardware information" on
+ [ $? = 1 ] && return;
+ result="$REPLY"
+ packages="packages = off"
+ partitions="partitions = off"
+ hardware="hardware = off"
+ for opt in $result; do
+ case $opt in
+ '"packages"') packages="packages = on";;
+ '"partitions"') partitions="partitions = on";;
+ '"hardware"') hardware="hardware = on";;
+ esac
+ done
+ get_next_filename $configdirectory/10.sys
+ cat > $next_filename <<EOF
+$packages
+$partitions
+$hardware
+# databases = all
+# backupdir = /var/backups/mysql
+# dbhost = localhost
+EOF
+ chmod 000 $next_filename
+}
+
diff --git a/ninjahelper b/ninjahelper
new file mode 100755
index 0000000..9631b80
--- /dev/null
+++ b/ninjahelper
@@ -0,0 +1,221 @@
+#!/bin/bash
+
+####################################################
+## Functions
+
+##
+## returns the next available file name given a file
+## in the form /etc/backup.d/10.sys
+## sets variable $returned_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
+}
+
+doradiobox() {
+ defaultchoice="red is.pretty on"
+ choices="green is_nice_too off blue i_love_blue off yellow is.bright off orange make.me.hungry off"
+ radioBox "radio title" "choose one color" $defaultchoice $choices
+ case $? in
+ 0) ;;
+ 1) echo "color choice cancelled..."; sleep 1;;
+ 255) echo "something went wrong, exiting..."
+ exit 1 ;;
+ esac
+ result="$REPLY"
+ msgBox "message title" "you said $result."
+}
+
+donew() {
+ menuBox "new action menu" "select an action to create" \
+ return "return to main menu" \
+ sys "general hardware and system info" \
+ mysql "mysql database backup" \
+ ldap "ldap database backup" \
+ rdiff "incremental filesystem backup"
+
+ [ $? = 1 ] && return;
+ result="$REPLY"
+ case "$result" in
+ "sys") sys_wizard;;
+ "mysql") mysql_wizard;;
+ "ldap") ldap_wizard;;
+ "rdiff") rdiff_wizard;;
+ "return") return;;
+ esac
+}
+
+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_run_test() {
+ backupninja --test --run $1
+ echo "Hit return to continue..."
+ read
+}
+
+do_disable() {
+ dir=`dirname $1`
+ base=`basename $1`
+ mv $dir/$base $dir/0-$base
+}
+
+do_enable() {
+ dir=`dirname $1`
+ base=`basename $1`
+ mv $dir/$base $dir/${base:2}
+}
+
+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:0:2}" == "0-" ]; 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") $EDITOR $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
+
+conffile="/etc/backupninja.conf"
+if [ ! -r "$conffile" ]; then
+ echo "Configuration file $conffile not found."
+ exit 1
+fi
+scriptdir=`grep scriptdirectory $conffile | awk '{print $3}'`
+if [ ! -n "$scriptdir" ]; then
+ echo "Cound not find entry 'scriptdirectory' in $conffile"
+ exit 1
+fi
+if [ ! -d "$scriptdir" ]; then
+ echo "Script directory $scriptdir not found."
+ exit 1
+fi
+configdirectory=`grep configdirectory $conffile | awk '{print $3}'`
+if [ ! -n "$configdirectory" ]; then
+ echo "Cound not find entry 'configdirectory' in $conffile"
+ exit 1
+fi
+if [ ! -d "$configdirectory" ]; then
+ echo "Configuration directory $configdirectory not found."
+ exit 1
+fi
+
+. $scriptdir/easydialog.sh
+
+if [ "$UID" != "0" ]; then
+ msgBox "warning" "ninjahelper must be run by root!"
+ exit 1
+fi
+
+for file in `find $scriptdir -follow -name '*.helper'`; do
+ . $file
+done
+
+setApplicationTitle "ninjahelper"
+setDimension 75 19
+
+#####################################################
+## main event loop
+
+while true; do
+
+menulist=
+action=
+let "i = 1"
+for file in `find $conf/etc/backup.d/ -type f | sort -n`; do
+ menulist="$menulist $i $file"
+ actions[$i]=$file
+ let "i += 1"
+done
+
+menuBox "main menu" "select an action to edit" $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