# HG changeset patch # User Heiko Schlittermann # Date 1256675467 -3600 # Node ID 1691a932eed1bfb788d52e7a0371d096d4ba41c4 # Parent c3c8a413c020414ef397eb160a8d43d605e0b42f transition to ftbackup and added debian files diff -r c3c8a413c020 -r 1691a932eed1 debian/README.Debian --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/README.Debian Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,6 @@ +ftbackup for Debian +------------------- + + + + -- Heiko Schlittermann Mon, 26 Oct 2009 23:42:43 +0100 diff -r c3c8a413c020 -r 1691a932eed1 debian/changelog --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/changelog Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,6 @@ +ftbackup (0.1-1) unstable; urgency=low + + * Initial release (Closes: #nnnn) + + -- Heiko Schlittermann Mon, 26 Oct 2009 23:42:43 +0100 + diff -r c3c8a413c020 -r 1691a932eed1 debian/compat --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/compat Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,1 @@ +7 diff -r c3c8a413c020 -r 1691a932eed1 debian/control --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/control Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,13 @@ +Source: ftbackup +Section: unknown +Priority: extra +Maintainer: Heiko Schlittermann +Build-Depends: debhelper (>= 7) +Standards-Version: 3.7.3 +Homepage: + +Package: ftbackup +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: + diff -r c3c8a413c020 -r 1691a932eed1 debian/copyright --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/copyright Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,24 @@ +This package was debianized by Heiko Schlittermann on +Mon, 26 Oct 2009 23:42:43 +0100. + +It was downloaded from + +Upstream Author(s): + + + + +Copyright: + + + + +License: + + + +The Debian packaging is (C) 2009, Heiko Schlittermann and +is licensed under the GPL, see `/usr/share/common-licenses/GPL'. + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. diff -r c3c8a413c020 -r 1691a932eed1 debian/cron.d.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cron.d.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,4 @@ +# +# Regular cron jobs for the ftbackup package +# +0 4 * * * root [ -x /usr/bin/ftbackup_maintenance ] && /usr/bin/ftbackup_maintenance diff -r c3c8a413c020 -r 1691a932eed1 debian/dirs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/dirs Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,2 @@ +usr/bin +usr/sbin diff -r c3c8a413c020 -r 1691a932eed1 debian/docs diff -r c3c8a413c020 -r 1691a932eed1 debian/emacsen-install.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/emacsen-install.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,45 @@ +#! /bin/sh -e +# /usr/lib/emacsen-common/packages/install/ftbackup + +# Written by Jim Van Zandt , borrowing heavily +# from the install scripts for gettext by Santiago Vila +# and octave by Dirk Eddelbuettel . + +FLAVOR=$1 +PACKAGE=ftbackup + +if [ ${FLAVOR} = emacs ]; then exit 0; fi + +echo install/${PACKAGE}: Handling install for emacsen flavor ${FLAVOR} + +#FLAVORTEST=`echo $FLAVOR | cut -c-6` +#if [ ${FLAVORTEST} = xemacs ] ; then +# SITEFLAG="-no-site-file" +#else +# SITEFLAG="--no-site-file" +#fi +FLAGS="${SITEFLAG} -q -batch -l path.el -f batch-byte-compile" + +ELDIR=/usr/share/emacs/site-lisp/${PACKAGE} +ELCDIR=/usr/share/${FLAVOR}/site-lisp/${PACKAGE} + +# Install-info-altdir does not actually exist. +# Maybe somebody will write it. +if test -x /usr/sbin/install-info-altdir; then + echo install/${PACKAGE}: install Info links for ${FLAVOR} + install-info-altdir --quiet --section "" "" --dirname=${FLAVOR} /usr/share/info/${PACKAGE}.info.gz +fi + +install -m 755 -d ${ELCDIR} +cd ${ELDIR} +FILES=`echo *.el` +cp ${FILES} ${ELCDIR} +cd ${ELCDIR} + +cat << EOF > path.el +(setq load-path (cons "." load-path) byte-compile-warnings nil) +EOF +${FLAVOR} ${FLAGS} ${FILES} +rm -f *.el path.el + +exit 0 diff -r c3c8a413c020 -r 1691a932eed1 debian/emacsen-remove.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/emacsen-remove.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,15 @@ +#!/bin/sh -e +# /usr/lib/emacsen-common/packages/remove/ftbackup + +FLAVOR=$1 +PACKAGE=ftbackup + +if [ ${FLAVOR} != emacs ]; then + if test -x /usr/sbin/install-info-altdir; then + echo remove/${PACKAGE}: removing Info links for ${FLAVOR} + install-info-altdir --quiet --remove --dirname=${FLAVOR} /usr/share/info/ftbackup.info.gz + fi + + echo remove/${PACKAGE}: purging byte-compiled files for ${FLAVOR} + rm -rf /usr/share/${FLAVOR}/site-lisp/${PACKAGE} +fi diff -r c3c8a413c020 -r 1691a932eed1 debian/emacsen-startup.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/emacsen-startup.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,25 @@ +;; -*-emacs-lisp-*- +;; +;; Emacs startup file, e.g. /etc/emacs/site-start.d/50ftbackup.el +;; for the Debian ftbackup package +;; +;; Originally contributed by Nils Naumann +;; Modified by Dirk Eddelbuettel +;; Adapted for dh-make by Jim Van Zandt + +;; The ftbackup package follows the Debian/GNU Linux 'emacsen' policy and +;; byte-compiles its elisp files for each 'emacs flavor' (emacs19, +;; xemacs19, emacs20, xemacs20...). The compiled code is then +;; installed in a subdirectory of the respective site-lisp directory. +;; We have to add this to the load-path: +(let ((package-dir (concat "/usr/share/" + (symbol-name flavor) + "/site-lisp/ftbackup"))) +;; If package-dir does not exist, the ftbackup package must have +;; removed but not purged, and we should skip the setup. + (when (file-directory-p package-dir) + (setq load-path (cons package-dir load-path)) + (autoload 'ftbackup-mode "ftbackup-mode" + "Major mode for editing ftbackup files." t) + (add-to-list 'auto-mode-alist '("\\.ftbackup$" . ftbackup-mode)))) + diff -r c3c8a413c020 -r 1691a932eed1 debian/ftbackup.default.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/ftbackup.default.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,10 @@ +# Defaults for ftbackup initscript +# sourced by /etc/init.d/ftbackup +# installed at /etc/default/ftbackup by the maintainer scripts + +# +# This is a POSIX shell fragment +# + +# Additional options that are passed to the Daemon. +DAEMON_OPTS="" diff -r c3c8a413c020 -r 1691a932eed1 debian/ftbackup.doc-base.EX --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/ftbackup.doc-base.EX Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,22 @@ +Document: ftbackup +Title: Debian ftbackup Manual +Author: +Abstract: This manual describes what ftbackup is + and how it can be used to + manage online manuals on Debian systems. +Section: unknown + +Format: debiandoc-sgml +Files: /usr/share/doc/ftbackup/ftbackup.sgml.gz + +Format: postscript +Files: /usr/share/doc/ftbackup/ftbackup.ps.gz + +Format: text +Files: /usr/share/doc/ftbackup/ftbackup.text.gz + +Format: HTML +Index: /usr/share/doc/ftbackup/html/index.html +Files: /usr/share/doc/ftbackup/html/*.html + + diff -r c3c8a413c020 -r 1691a932eed1 debian/init.d.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/init.d.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,157 @@ +#! /bin/sh +# +# skeleton example file to build /etc/init.d/ scripts. +# This file should be used to construct scripts for /etc/init.d. +# +# Written by Miquel van Smoorenburg . +# Modified for Debian +# by Ian Murdock . +# Further changes by Javier Fernandez-Sanguino +# +# Version: @(#)skeleton 1.9 26-Feb-2001 miquels@cistron.nl +# + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/ftbackup +NAME=ftbackup +DESC=ftbackup + +test -x $DAEMON || exit 0 + +LOGDIR=/var/log/ftbackup +PIDFILE=/var/run/$NAME.pid +DODTIME=1 # Time to wait for the server to die, in seconds + # If this value is set too low you might not + # let some servers to die gracefully and + # 'restart' will not work + +# Include ftbackup defaults if available +if [ -f /etc/default/ftbackup ] ; then + . /etc/default/ftbackup +fi + +set -e + +running_pid() +{ + # Check if a given process pid's cmdline matches a given name + pid=$1 + name=$2 + [ -z "$pid" ] && return 1 + [ ! -d /proc/$pid ] && return 1 + cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1` + # Is this the expected child? + [ "$cmd" != "$name" ] && return 1 + return 0 +} + +running() +{ +# Check if the process is running looking at /proc +# (works for all users) + + # No pidfile, probably no daemon present + [ ! -f "$PIDFILE" ] && return 1 + # Obtain the pid and check it against the binary name + pid=`cat $PIDFILE` + running_pid $pid $DAEMON || return 1 + return 0 +} + +force_stop() { +# Forcefully kill the process + [ ! -f "$PIDFILE" ] && return + if running ; then + kill -15 $pid + # Is it really dead? + [ -n "$DODTIME" ] && sleep "$DODTIME"s + if running ; then + kill -9 $pid + [ -n "$DODTIME" ] && sleep "$DODTIME"s + if running ; then + echo "Cannot kill $LABEL (pid=$pid)!" + exit 1 + fi + fi + fi + rm -f $PIDFILE + return 0 +} + +case "$1" in + start) + echo -n "Starting $DESC: " + start-stop-daemon --start --quiet --pidfile $PIDFILE \ + --exec $DAEMON -- $DAEMON_OPTS + if running ; then + echo "$NAME." + else + echo " ERROR." + fi + ;; + stop) + echo -n "Stopping $DESC: " + start-stop-daemon --stop --quiet --pidfile $PIDFILE \ + --exec $DAEMON + echo "$NAME." + ;; + force-stop) + echo -n "Forcefully stopping $DESC: " + force_stop + if ! running ; then + echo "$NAME." + else + echo " ERROR." + fi + ;; + #reload) + # + # If the daemon can reload its config files on the fly + # for example by sending it SIGHUP, do it here. + # + # If the daemon responds to changes in its config file + # directly anyway, make this a do-nothing entry. + # + # echo "Reloading $DESC configuration files." + # start-stop-daemon --stop --signal 1 --quiet --pidfile \ + # /var/run/$NAME.pid --exec $DAEMON + #;; + force-reload) + # + # If the "reload" option is implemented, move the "force-reload" + # option to the "reload" entry above. If not, "force-reload" is + # just the same as "restart" except that it does nothing if the + # daemon isn't already running. + # check wether $DAEMON is running. If so, restart + start-stop-daemon --stop --test --quiet --pidfile \ + /var/run/$NAME.pid --exec $DAEMON \ + && $0 restart \ + || exit 0 + ;; + restart) + echo -n "Restarting $DESC: " + start-stop-daemon --stop --quiet --pidfile \ + /var/run/$NAME.pid --exec $DAEMON + [ -n "$DODTIME" ] && sleep $DODTIME + start-stop-daemon --start --quiet --pidfile \ + /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS + echo "$NAME." + ;; + status) + echo -n "$LABEL is " + if running ; then + echo "running" + else + echo " not running." + exit 1 + fi + ;; + *) + N=/etc/init.d/$NAME + # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $N {start|stop|restart|force-reload|status|force-stop}" >&2 + exit 1 + ;; +esac + +exit 0 diff -r c3c8a413c020 -r 1691a932eed1 debian/init.d.lsb.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/init.d.lsb.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,296 @@ +#!/bin/sh +# +# Example init.d script with LSB support. +# +# Please read this init.d carefully and modify the sections to +# adjust it to the program you want to run. +# +# Copyright (c) 2007 Javier Fernandez-Sanguino +# +# This is free software; you may 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, +# or (at your option) any later version. +# +# This 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. +# +# You should have received a copy of the GNU General Public License with +# the Debian operating system, in /usr/share/common-licenses/GPL; if +# not, write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA +# +### BEGIN INIT INFO +# Provides: ftbackup +# Required-Start: $network $local_fs +# Required-Stop: +# Should-Start: $named +# Should-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: +# Description: +# <...> +# <...> +### END INIT INFO + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +DAEMON=/usr/sbin/ftbackup # Introduce the server's location here +NAME=#PACKAGE # Introduce the short server's name here +DESC=#PACKAGE # Introduce a short description here +LOGDIR=/var/log/ftbackup # Log directory to use + +PIDFILE=/var/run/$NAME.pid + +test -x $DAEMON || exit 0 + +. /lib/lsb/init-functions + +# Default options, these can be overriden by the information +# at /etc/default/$NAME +DAEMON_OPTS="" # Additional options given to the server + +DIETIME=10 # Time to wait for the server to die, in seconds + # If this value is set too low you might not + # let some servers to die gracefully and + # 'restart' will not work + +#STARTTIME=2 # Time to wait for the server to start, in seconds + # If this value is set each time the server is + # started (on start or restart) the script will + # stall to try to determine if it is running + # If it is not set and the server takes time + # to setup a pid file the log message might + # be a false positive (says it did not start + # when it actually did) + +LOGFILE=$LOGDIR/$NAME.log # Server logfile +#DAEMONUSER=ftbackup # Users to run the daemons as. If this value + # is set start-stop-daemon will chuid the server + +# Include defaults if available +if [ -f /etc/default/$NAME ] ; then + . /etc/default/$NAME +fi + +# Use this if you want the user to explicitly set 'RUN' in +# /etc/default/ +#if [ "x$RUN" != "xyes" ] ; then +# log_failure_msg "$NAME disabled, please adjust the configuration to your needs " +# log_failure_msg "and then set RUN to 'yes' in /etc/default/$NAME to enable it." +# exit 1 +#fi + +# Check that the user exists (if we set a user) +# Does the user exist? +if [ -n "$DAEMONUSER" ] ; then + if getent passwd | grep -q "^$DAEMONUSER:"; then + # Obtain the uid and gid + DAEMONUID=`getent passwd |grep "^$DAEMONUSER:" | awk -F : '{print $3}'` + DAEMONGID=`getent passwd |grep "^$DAEMONUSER:" | awk -F : '{print $4}'` + else + log_failure_msg "The user $DAEMONUSER, required to run $NAME does not exist." + exit 1 + fi +fi + + +set -e + +running_pid() { +# Check if a given process pid's cmdline matches a given name + pid=$1 + name=$2 + [ -z "$pid" ] && return 1 + [ ! -d /proc/$pid ] && return 1 + cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1` + # Is this the expected server + [ "$cmd" != "$name" ] && return 1 + return 0 +} + +running() { +# Check if the process is running looking at /proc +# (works for all users) + + # No pidfile, probably no daemon present + [ ! -f "$PIDFILE" ] && return 1 + pid=`cat $PIDFILE` + running_pid $pid $DAEMON || return 1 + return 0 +} + +start_server() { +# Start the process using the wrapper + if [ -z "$DAEMONUSER" ] ; then + start_daemon -p $PIDFILE $DAEMON -- $DAEMON_OPTS + errcode=$? + else +# if we are using a daemonuser then change the user id + start-stop-daemon --start --quiet --pidfile $PIDFILE \ + --chuid $DAEMONUSER \ + --exec $DAEMON -- $DAEMON_OPTS + errcode=$? + fi + return $errcode +} + +stop_server() { +# Stop the process using the wrapper + if [ -z "$DAEMONUSER" ] ; then + killproc -p $PIDFILE $DAEMON + errcode=$? + else +# if we are using a daemonuser then look for process that match + start-stop-daemon --stop --quiet --pidfile $PIDFILE \ + --user $DAEMONUSER \ + --exec $DAEMON + errcode=$? + fi + + return $errcode +} + +reload_server() { + [ ! -f "$PIDFILE" ] && return 1 + pid=pidofproc $PIDFILE # This is the daemon's pid + # Send a SIGHUP + kill -1 $pid + return $? +} + +force_stop() { +# Force the process to die killing it manually + [ ! -e "$PIDFILE" ] && return + if running ; then + kill -15 $pid + # Is it really dead? + sleep "$DIETIME"s + if running ; then + kill -9 $pid + sleep "$DIETIME"s + if running ; then + echo "Cannot kill $NAME (pid=$pid)!" + exit 1 + fi + fi + fi + rm -f $PIDFILE +} + + +case "$1" in + start) + log_daemon_msg "Starting $DESC " "$NAME" + # Check if it's running first + if running ; then + log_progress_msg "apparently already running" + log_end_msg 0 + exit 0 + fi + if start_server ; then + # NOTE: Some servers might die some time after they start, + # this code will detect this issue if STARTTIME is set + # to a reasonable value + [ -n "$STARTTIME" ] && sleep $STARTTIME # Wait some time + if running ; then + # It's ok, the server started and is running + log_end_msg 0 + else + # It is not running after we did start + log_end_msg 1 + fi + else + # Either we could not start it + log_end_msg 1 + fi + ;; + stop) + log_daemon_msg "Stopping $DESC" "$NAME" + if running ; then + # Only stop the server if we see it running + errcode=0 + stop_server || errcode=$? + log_end_msg $errcode + else + # If it's not running don't do anything + log_progress_msg "apparently not running" + log_end_msg 0 + exit 0 + fi + ;; + force-stop) + # First try to stop gracefully the program + $0 stop + if running; then + # If it's still running try to kill it more forcefully + log_daemon_msg "Stopping (force) $DESC" "$NAME" + errcode=0 + force_stop || errcode=$? + log_end_msg $errcode + fi + ;; + restart|force-reload) + log_daemon_msg "Restarting $DESC" "$NAME" + errcode=0 + stop_server || errcode=$? + # Wait some sensible amount, some server need this + [ -n "$DIETIME" ] && sleep $DIETIME + start_server || errcode=$? + [ -n "$STARTTIME" ] && sleep $STARTTIME + running || errcode=$? + log_end_msg $errcode + ;; + status) + + log_daemon_msg "Checking status of $DESC" "$NAME" + if running ; then + log_progress_msg "running" + log_end_msg 0 + else + log_progress_msg "apparently not running" + log_end_msg 1 + exit 1 + fi + ;; + # Use this if the daemon cannot reload + reload) + log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon" + log_warning_msg "cannot re-read the config file (use restart)." + ;; + # And this if it cann + #reload) + # + # If the daemon can reload its config files on the fly + # for example by sending it SIGHUP, do it here. + # + # If the daemon responds to changes in its config file + # directly anyway, make this a do-nothing entry. + # + # log_daemon_msg "Reloading $DESC configuration files" "$NAME" + # if running ; then + # reload_server + # if ! running ; then + # Process died after we tried to reload + # log_progress_msg "died on reload" + # log_end_msg 1 + # exit 1 + # fi + # else + # log_progress_msg "server is not running" + # log_end_msg 1 + # exit 1 + # fi + #;; + + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" >&2 + exit 1 + ;; +esac + +exit 0 diff -r c3c8a413c020 -r 1691a932eed1 debian/manpage.1.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/manpage.1.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,59 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH FTBACKUP SECTION "October 26, 2009" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +ftbackup \- program to do something +.SH SYNOPSIS +.B ftbackup +.RI [ options ] " files" ... +.br +.B bar +.RI [ options ] " files" ... +.SH DESCRIPTION +This manual page documents briefly the +.B ftbackup +and +.B bar +commands. +.PP +.\" TeX users may be more comfortable with the \fB\fP and +.\" \fI\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBftbackup\fP is a program that... +.SH OPTIONS +These programs follow the usual GNU command line syntax, with long +options starting with two dashes (`-'). +A summary of options is included below. +For a complete description, see the Info files. +.TP +.B \-h, \-\-help +Show summary of options. +.TP +.B \-v, \-\-version +Show version of program. +.SH SEE ALSO +.BR bar (1), +.BR baz (1). +.br +The programs are documented fully by +.IR "The Rise and Fall of a Fooish Bar" , +available via the Info system. +.SH AUTHOR +ftbackup was written by . +.PP +This manual page was written by Heiko Schlittermann , +for the Debian project (but may be used by others). diff -r c3c8a413c020 -r 1691a932eed1 debian/manpage.sgml.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/manpage.sgml.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,156 @@ + manpage.1'. You may view + the manual page with: `docbook-to-man manpage.sgml | nroff -man | + less'. A typical entry in a Makefile or Makefile.am is: + +manpage.1: manpage.sgml + docbook-to-man $< > $@ + + + The docbook-to-man binary is found in the docbook-to-man package. + Please remember that if you create the nroff version in one of the + debian/rules file targets (such as build), you will need to include + docbook-to-man in your Build-Depends control field. + + --> + + + FIRSTNAME"> + SURNAME"> + + October 26, 2009"> + + SECTION"> + hs@schlittermann.de"> + + FTBACKUP"> + + + Debian"> + GNU"> + GPL"> +]> + + + +
+ &dhemail; +
+ + &dhfirstname; + &dhsurname; + + + 2003 + &dhusername; + + &dhdate; +
+ + &dhucpackage; + + &dhsection; + + + &dhpackage; + + program to do something + + + + &dhpackage; + + + + + + + + DESCRIPTION + + This manual page documents briefly the + &dhpackage; and bar + commands. + + This manual page was written for the &debian; distribution + because the original program does not have a manual page. + Instead, it has documentation in the &gnu; + Info format; see below. + + &dhpackage; is a program that... + + + + OPTIONS + + These programs follow the usual &gnu; command line syntax, + with long options starting with two dashes (`-'). A summary of + options is included below. For a complete description, see the + Info files. + + + + + + + + Show summary of options. + + + + + + + + Show version of program. + + + + + + SEE ALSO + + bar (1), baz (1). + + The programs are documented fully by The Rise and + Fall of a Fooish Bar available via the + Info system. + + + AUTHOR + + This manual page was written by &dhusername; &dhemail; for + the &debian; system (but may be used by others). Permission is + granted to copy, distribute and/or modify this document under + the terms of the &gnu; General Public License, Version 2 any + later version published by the Free Software Foundation. + + + On Debian systems, the complete text of the GNU General Public + License can be found in /usr/share/common-licenses/GPL. + + + +
+ + + + diff -r c3c8a413c020 -r 1691a932eed1 debian/manpage.xml.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/manpage.xml.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,291 @@ + +.
will be generated. You may view the +manual page with: nroff -man .
| less'. A typical entry +in a Makefile or Makefile.am is: + +DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl +XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0" + +manpage.1: manpage.xml + $(XP) $(DB2MAN) $< + +The xsltproc binary is found in the xsltproc package. The XSL files are in +docbook-xsl. A description of the parameters you can use can be found in the +docbook-xsl-doc-* packages. Please remember that if you create the nroff +version in one of the debian/rules file targets (such as build), you will need +to include xsltproc and docbook-xsl in your Build-Depends control field. +Alternatively use the xmlto command/package. That will also automatically +pull in xsltproc and docbook-xsl. + +Notes for using docbook2x: docbook2x-man does not automatically create the +AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as + ... . + +To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections +read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be +found in the docbook-xsl-doc-html package. + +Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` + +General documentation about man-pages and man-page-formatting: +man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + +--> + + + + + + + + + + + + + +]> + + + + &dhtitle; + &dhpackage; + + + &dhfirstname; + &dhsurname; + Wrote this manpage for the Debian system. +
+ &dhemail; +
+
+
+ + 2007 + &dhusername; + + + This manual page was written for the Debian system + (but may be used by others). + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU General Public License, + Version 2 or (at your option) any later version published by + the Free Software Foundation. + On Debian systems, the complete text of the GNU General Public + License can be found in + /usr/share/common-licenses/GPL. + +
+ + &dhucpackage; + &dhsection; + + + &dhpackage; + program to do something + + + + &dhpackage; + + + + + + + + + this + + + + + + + + this + that + + + + + &dhpackage; + + + + + + + + + + + + + + + + + + + DESCRIPTION + This manual page documents briefly the + &dhpackage; and bar + commands. + This manual page was written for the Debian distribution + because the original program does not have a manual page. + Instead, it has documentation in the GNU + info + 1 + format; see below. + &dhpackage; is a program that... + + + OPTIONS + The program follows the usual GNU command line syntax, + with long options starting with two dashes (`-'). A summary of + options is included below. For a complete description, see the + + info + 1 + files. + + + + + + + Does this and that. + + + + + + + Show summary of options. + + + + + + + Show version of program. + + + + + + FILES + + + /etc/foo.conf + + The system-wide configuration file to control the + behaviour of &dhpackage;. See + + foo.conf + 5 + for further details. + + + + ${HOME}/.foo.conf + + The per-user configuration file to control the + behaviour of &dhpackage;. See + + foo.conf + 5 + for further details. + + + + + + ENVIONMENT + + + FOO_CONF + + If used, the defined file is used as configuration + file (see also ). + + + + + + DIAGNOSTICS + The following diagnostics may be issued + on stderr: + + + Bad configuration file. Exiting. + + The configuration file seems to contain a broken configuration + line. Use the option, to get more info. + + + + + &dhpackage; provides some return codes, that can + be used in scripts: + + Code + Diagnostic + + 0 + Program exited successfully. + + + 1 + The configuration file seems to be broken. + + + + + + BUGS + The program is currently limited to only work + with the foobar library. + The upstreams BTS can be found + at . + + + SEE ALSO + + + bar + 1 + , + baz + 1 + , + foo.conf + 5 + + The programs are documented fully by The Rise and + Fall of a Fooish Bar available via the + info + 1 + system. + +
+ diff -r c3c8a413c020 -r 1691a932eed1 debian/menu.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/menu.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,2 @@ +?package(ftbackup):needs="X11|text|vc|wm" section="Applications/see-menu-manual"\ + title="ftbackup" command="/usr/bin/ftbackup" diff -r c3c8a413c020 -r 1691a932eed1 debian/postinst.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/postinst.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,41 @@ +#!/bin/sh +# postinst script for ftbackup +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff -r c3c8a413c020 -r 1691a932eed1 debian/postrm.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/postrm.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,39 @@ +#!/bin/sh +# postrm script for ftbackup +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff -r c3c8a413c020 -r 1691a932eed1 debian/preinst.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/preinst.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,37 @@ +#!/bin/sh +# preinst script for ftbackup +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `install' +# * `install' +# * `upgrade' +# * `abort-upgrade' +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + install|upgrade) + ;; + + abort-upgrade) + ;; + + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff -r c3c8a413c020 -r 1691a932eed1 debian/prerm.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/prerm.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,40 @@ +#!/bin/sh +# prerm script for ftbackup +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + remove|upgrade|deconfigure) + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff -r c3c8a413c020 -r 1691a932eed1 debian/rules --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/rules Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,91 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + + + + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. + + touch configure-stamp + + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + #docbook-to-man debian/ftbackup.sgml > ftbackup.1 + + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + $(MAKE) clean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/ftbackup. + $(MAKE) DESTDIR=$(CURDIR)/debian/ftbackup install + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples +# dh_install +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_python +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff -r c3c8a413c020 -r 1691a932eed1 debian/watch.ex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/watch.ex Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,23 @@ +# Example watch control file for uscan +# Rename this file to "watch" and then you can run the "uscan" command +# to check for upstream updates and more. +# See uscan(1) for format + +# Compulsory line, this is a version 3 file +version=3 + +# Uncomment to examine a Webpage +# +#http://www.example.com/downloads.php ftbackup-(.*)\.tar\.gz + +# Uncomment to examine a Webserver directory +#http://www.example.com/pub/ftbackup-(.*)\.tar\.gz + +# Uncommment to examine a FTP server +#ftp://ftp.example.com/pub/ftbackup-(.*)\.tar\.gz debian uupdate + +# Uncomment to find new files on sourceforge, for devscripts >= 2.9 +# http://sf.net/ftbackup/ftbackup-(.*)\.tar\.gz + +# Uncomment to find new files on GooglePages +# http://example.googlepages.com/foo.html ftbackup-(.*)\.tar\.gz diff -r c3c8a413c020 -r 1691a932eed1 ftbackup --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ftbackup Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,321 @@ +#! /usr/bin/perl +use strict; +use warnings; + +use IO::File; +use File::Basename; +use Net::FTP; +use Perl6::Slurp; +use Getopt::Long; +use Sys::Hostname; +use Pod::Usage; +use POSIX qw(strftime);; +use English qw(-no_match_vars); +use 5.10.0; +use if $ENV{DEBUG} => qw(Smart::Comments); + +$ENV{LC_ALL} = "C"; + +my $ME = basename $0; + +my @CONFIGS = ("/etc/$ME", "$ENV{HOME}/.$ME", "$ME.conf"); + +my $NODE = hostname; +my $NOW = time(); + +my $opt_level = 0; +my $opt_today = strftime("%F", localtime $NOW); +my @opt_debug = (); +my $opt_verbose = 0; +my $opt_dry = 0; +my $opt_force = 0; + +sub get_configs(@); +sub get_candidates(); +sub verbose(@); + +our @AT_EXIT; +END { $_->() foreach @AT_EXIT }; +$SIG{INT} = sub { warn "Got signal INT\n"; exit 1 }; + +my %CONFIG = ( + FTP_DIR => "backup/daily/$NODE", + FTP_PASSIVE => 1, + FULL_CYCLE => 7, # not used +); + +MAIN: { + GetOptions( + "l|level=i" => \$opt_level, + "d|debug:s" => sub { push @opt_debug, split /,/, $_[1] }, + "h|help" => sub { pod2usage(-exit => 0, -verbose => 1) }, + "m|man" => sub { pod2usage(-exit => 0, -verbose => 3) }, + "v|verbose" => \$opt_verbose, + "dry" => \$opt_dry, + "f|force" => \$opt_force, + ) or pod2usage; + + my %cf = (%CONFIG, get_configs(@CONFIGS)); + my @dev = get_candidates(); + ### current candiates: @dev + + my $ftp = new FTP($cf{FTP_HOST}, + Passive => $cf{FTP_PASSIVE}, + Debug => @opt_debug ~~ /^ftp$/) or die $@; + $ftp->login or die $ftp->message; + $ftp->try(binary => ()); + $ftp->try(mkpath => $cf{FTP_DIR}); + $ftp->try(cwd => $cf{FTP_DIR}); + + given ($opt_level) { + when(0) { + $ftp->try(mkpath => $opt_today); + $ftp->try(cwd => $opt_today); + } + default { + # find the last full backup directory + my $last_full = (reverse sort grep /^\d{4}-\d{2}-\d{2}$/, $ftp->ls)[0]; + die "no last full backup found in @{[$ftp->pwd]}\n" + if not $last_full; + $ftp->try(cwd => $last_full); + } + } + + # now sitting inside the directory for the last full backup + verbose "Now in @{[$ftp->pwd]}.\n"; + + # and now we can start doing something with our filesystems + foreach my $dev (@dev) { + + my $file = basename($dev->{dev}) . "." + . strftime("%F_%R", localtime $NOW) + . ".$opt_level.ssl"; + my $label = "$NODE:" . basename($dev->{rdev}); + verbose "Working on $dev->{dev} as $dev->{rdev}, stored as $file\n"; + next if $opt_dry; + + ## complain if there is already a full backup in this + ## sequence + ##die "level 0 dir should be empty\n" if @{$ftp->try(ls => "*.0.*")}; + + # For LVM do a snapshot, for regular partitions + # do nothing. But anyway the device to dump is named in $dev->{dump} + if ($dev->{lvm}) { + # we can do a snapshot + # FIXME: calculate the size + my $snap = "$dev->{lvm}{path}-0"; + + verbose "Creating snapshot $snap\n"; + system($_ = "lvcreate -s -L 1G -n $snap $dev->{lvm}{path} >/dev/null"); + die "failed system command: $_\n" if $?; + + $dev->{cleanup} = sub { system "lvdisplay $snap &>/dev/null" + . " && lvremove -f $snap >/dev/null" }; + push @AT_EXIT, $dev->{cleanup}; + + (my $device) = (grep /lv name/i, `lvdisplay $snap`)[0] =~ /(\S+)\s*$/; + + system($_ = "fsck -f -C0 -y $device"); + warn "fsck on $device (using: $_) failed\n" if $?; + + ($dev->{dump}) = $device; + + } + else { + $dev->{dump} = $dev->{rdev} + } + + ### $dev + + $ENV{key} = $cf{KEY}; + my $dumper = open(my $dump, "-|") or do { + my $head = <<__; +#! /bin/bash +if test "\$1" = "--info"; then + cat <<___ +NODE : $NODE +DATE : $NOW @{[localtime $NOW]} +LEVEL : $opt_level +DEVICE : $dev->{dev} +REAL_DEVICE: $dev->{rdev} +MOUNTPOINT : $dev->{mountpoint} +FSTYPE : $dev->{fstype} +___ + exit 0 +fi +tail -c XXXXX \$0 | openssl enc -d -blowfish "\$@" +exit + +__ + # adjust the placeholder + $head =~ s/XXXXX/sprintf "% 5s", "+" . (length($head) +1)/e; + print $head; + exec "dump -$opt_level -L $label -f- -u -z6 $dev->{dump}" + . "| openssl enc -pass env:key -salt -blowfish"; + die "Can't exec dumper\n"; + }; + + $ftp->try(put => $dump, $file); + $dev->{cleanup}->() if $dev->{cleanup}; + verbose "Done.\n"; + } + +} + +sub verbose(@) { + return if not $opt_verbose; + print @_; +} + +sub get_candidates() { +# return the list of backup candidates + + my @dev; + + # later we need the major of the device mapper + my $dev_mapper = 0; + $_ = (grep /device.mapper/, slurp("/proc/devices"))[0] + and $dev_mapper = (split)[0]; + + foreach (slurp("/etc/fstab")) { + my ($dev, $mp, $fstype, $options, $dump, $check) + = split; + next if not $dump; + + # $dev does not have to contain the real device + my $rdev = $dev; + if ($dev ~~ /^(LABEL|UUID)=/) { + chomp($rdev = `blkid -c /dev/null -o device -t '$dev'`); + } + $rdev = readlink $rdev while -l $rdev; + + # if it's LVM we gather more information (to support snapshots) + # FIXME: could have used `lvdisplay -c' + my $lvm; + if ((stat $rdev)[6] >> 8 == $dev_mapper) { + @{$lvm}{qw/vg lv/} = map { s/--/-/g; $_ } basename($rdev) =~ /(.+[^-])-([^-].+)/; + $lvm->{path} = "$lvm->{vg}/$lvm->{lv}"; + } + + push @dev, { + dev => $dev, + rdev => $rdev, + mountpoint => $mp, + fstype => $fstype, + lvm => $lvm, + }; + } + + return @dev; +} + +sub get_configs(@) { + local $_; + my %r = (); + foreach (grep {-f} map { (-d) ? glob("$_/*") : $_ } @_) { + + # check permission and ownership + { + my $p = (stat)[2] & 07777; + my $u = (stat _)[4]; + die "$ME: $_ has wrong permissions: found @{[sprintf '%04o', $p]}, need 0600\n" + if $p != 0600; + die "$ME: owner of $_ ($u) is not the EUID ($EUID) of this process\n" + if (stat _)[4] != $EUID; + + # FIXME: should check the containing directories too! + }; + + my $f = new IO::File $_ or die "Can't open $_: $!\n"; + my %h = map { split /\s*=\s*/, $_, 2 } grep {!/^\s*#/ and /=/} <$f>; + map { chomp } values %h; + %r = (%r, %h); + } + return %r; +} + +{ package FTP; + use strict; + use warnings; + use base qw(Net::FTP); + + sub new { + my $class = shift; + return bless Net::FTP->new(@_) => $class; + } + + sub try { + my $self = shift; + my $func = shift; + $self->$func(@_) + or die "FTP $func failed: " . $self->message . "\n"; + } + + sub mkpath { + my $self = shift; + my $current = $self->pwd(); + foreach (split /\/+/, $_[0]) { + next if $self->cwd($_); + return undef if not $self->message ~~ /no such .*dir/i; + return undef if not $self->SUPER::mkdir($_); + return undef if not $self->cwd($_); + } + $self->cwd($current); + } +} + +__END__ + +=head1 NAME + +py2b - backup tool + +=head1 SYNOPSIS + + py2b [--level ] [options] + +=head1 OPTIONS + +=over + +=item B<-d>|B<--debug> [I] + +Enables debugging for the specified items (comma separated). +If no item is specified, just some debugging is done. + +Valid items are B and currently nothing else. + +Even more debugging is shown using the DEBUG=1 environment setting. + +=item B<-f>|B<--force> + +Use more power (e.g. overwrite a previous level backup and remove all +invalidated other backups). (default: 0) + +=item B<-l>|B<--level> I + +The backup level. Level other than "0" needs a previous +level 0 (full) backup. (default: 0) + +=item B<-v>|B<--verbose> + +Be verbose. (default: no) + +=back + +=head1 FILES + +The config files are searched in the following places: + + /etc/py2b + ~/.py2b + ./py2b.conf + +If the location is a directory, all (not hidden) files in this directory are +considered to be config, if the location a file itself, this is considered to +be a config file. The config files have to be mode 0600 and they have to be +owned by the EUID running the process. + +=cut + +# vim:sts=4 sw=4 aw ai sm: diff -r c3c8a413c020 -r 1691a932eed1 ftbackup.conf.example --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ftbackup.conf.example Tue Oct 27 21:31:07 2009 +0100 @@ -0,0 +1,16 @@ +# example config +# the commented values are the built in defaults + +# The encryption key +KEY = + +# FTP-Server hostname +FTP_HOST = + +# FTP-Server base directory +# the following expansion work: +# $NODE +# FTP_DIR = backups/daily/$NODE + +# if we need passive mode for file transfer +# FTP_PASSIVE = 1 diff -r c3c8a413c020 -r 1691a932eed1 py2b --- a/py2b Mon Oct 26 23:35:18 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,321 +0,0 @@ -#! /usr/bin/perl -use strict; -use warnings; - -use IO::File; -use File::Basename; -use Net::FTP; -use Perl6::Slurp; -use Getopt::Long; -use Sys::Hostname; -use Pod::Usage; -use POSIX qw(strftime);; -use English qw(-no_match_vars); -use 5.10.0; -use if $ENV{DEBUG} => qw(Smart::Comments); - -$ENV{LC_ALL} = "C"; - -my $ME = basename $0; - -my @CONFIGS = ("/etc/$ME", "$ENV{HOME}/.$ME", "$ME.conf"); - -my $NODE = hostname; -my $NOW = time(); - -my $opt_level = 0; -my $opt_today = strftime("%F", localtime $NOW); -my @opt_debug = (); -my $opt_verbose = 0; -my $opt_dry = 0; -my $opt_force = 0; - -sub get_configs(@); -sub get_candidates(); -sub verbose(@); - -our @AT_EXIT; -END { $_->() foreach @AT_EXIT }; -$SIG{INT} = sub { warn "Got signal INT\n"; exit 1 }; - -my %CONFIG = ( - FTP_DIR => "backup/daily/$NODE", - FTP_PASSIVE => 1, - FULL_CYCLE => 7, # not used -); - -MAIN: { - GetOptions( - "l|level=i" => \$opt_level, - "d|debug:s" => sub { push @opt_debug, split /,/, $_[1] }, - "h|help" => sub { pod2usage(-exit => 0, -verbose => 1) }, - "m|man" => sub { pod2usage(-exit => 0, -verbose => 3) }, - "v|verbose" => \$opt_verbose, - "dry" => \$opt_dry, - "f|force" => \$opt_force, - ) or pod2usage; - - my %cf = (%CONFIG, get_configs(@CONFIGS)); - my @dev = get_candidates(); - ### current candiates: @dev - - my $ftp = new FTP($cf{FTP_HOST}, - Passive => $cf{FTP_PASSIVE}, - Debug => @opt_debug ~~ /^ftp$/) or die $@; - $ftp->login or die $ftp->message; - $ftp->try(binary => ()); - $ftp->try(mkpath => $cf{FTP_DIR}); - $ftp->try(cwd => $cf{FTP_DIR}); - - given ($opt_level) { - when(0) { - $ftp->try(mkpath => $opt_today); - $ftp->try(cwd => $opt_today); - } - default { - # find the last full backup directory - my $last_full = (reverse sort grep /^\d{4}-\d{2}-\d{2}$/, $ftp->ls)[0]; - die "no last full backup found in @{[$ftp->pwd]}\n" - if not $last_full; - $ftp->try(cwd => $last_full); - } - } - - # now sitting inside the directory for the last full backup - verbose "Now in @{[$ftp->pwd]}.\n"; - - # and now we can start doing something with our filesystems - foreach my $dev (@dev) { - - my $file = basename($dev->{dev}) . "." - . strftime("%F_%R", localtime $NOW) - . ".$opt_level.ssl"; - my $label = "$NODE:" . basename($dev->{rdev}); - verbose "Working on $dev->{dev} as $dev->{rdev}, stored as $file\n"; - next if $opt_dry; - - ## complain if there is already a full backup in this - ## sequence - ##die "level 0 dir should be empty\n" if @{$ftp->try(ls => "*.0.*")}; - - # For LVM do a snapshot, for regular partitions - # do nothing. But anyway the device to dump is named in $dev->{dump} - if ($dev->{lvm}) { - # we can do a snapshot - # FIXME: calculate the size - my $snap = "$dev->{lvm}{path}-0"; - - verbose "Creating snapshot $snap\n"; - system($_ = "lvcreate -s -L 1G -n $snap $dev->{lvm}{path} >/dev/null"); - die "failed system command: $_\n" if $?; - - $dev->{cleanup} = sub { system "lvdisplay $snap &>/dev/null" - . " && lvremove -f $snap >/dev/null" }; - push @AT_EXIT, $dev->{cleanup}; - - (my $device) = (grep /lv name/i, `lvdisplay $snap`)[0] =~ /(\S+)\s*$/; - - system($_ = "fsck -f -C0 -y $device"); - warn "fsck on $device (using: $_) failed\n" if $?; - - ($dev->{dump}) = $device; - - } - else { - $dev->{dump} = $dev->{rdev} - } - - ### $dev - - $ENV{key} = $cf{KEY}; - my $dumper = open(my $dump, "-|") or do { - my $head = <<__; -#! /bin/bash -if test "\$1" = "--info"; then - cat <<___ -NODE : $NODE -DATE : $NOW @{[localtime $NOW]} -LEVEL : $opt_level -DEVICE : $dev->{dev} -REAL_DEVICE: $dev->{rdev} -MOUNTPOINT : $dev->{mountpoint} -FSTYPE : $dev->{fstype} -___ - exit 0 -fi -tail -c XXXXX \$0 | openssl enc -d -blowfish "\$@" -exit - -__ - # adjust the placeholder - $head =~ s/XXXXX/sprintf "% 5s", "+" . (length($head) +1)/e; - print $head; - exec "dump -$opt_level -L $label -f- -u -z6 $dev->{dump}" - . "| openssl enc -pass env:key -salt -blowfish"; - die "Can't exec dumper\n"; - }; - - $ftp->try(put => $dump, $file); - $dev->{cleanup}->() if $dev->{cleanup}; - verbose "Done.\n"; - } - -} - -sub verbose(@) { - return if not $opt_verbose; - print @_; -} - -sub get_candidates() { -# return the list of backup candidates - - my @dev; - - # later we need the major of the device mapper - my $dev_mapper = 0; - $_ = (grep /device.mapper/, slurp("/proc/devices"))[0] - and $dev_mapper = (split)[0]; - - foreach (slurp("/etc/fstab")) { - my ($dev, $mp, $fstype, $options, $dump, $check) - = split; - next if not $dump; - - # $dev does not have to contain the real device - my $rdev = $dev; - if ($dev ~~ /^(LABEL|UUID)=/) { - chomp($rdev = `blkid -c /dev/null -o device -t '$dev'`); - } - $rdev = readlink $rdev while -l $rdev; - - # if it's LVM we gather more information (to support snapshots) - # FIXME: could have used `lvdisplay -c' - my $lvm; - if ((stat $rdev)[6] >> 8 == $dev_mapper) { - @{$lvm}{qw/vg lv/} = map { s/--/-/g; $_ } basename($rdev) =~ /(.+[^-])-([^-].+)/; - $lvm->{path} = "$lvm->{vg}/$lvm->{lv}"; - } - - push @dev, { - dev => $dev, - rdev => $rdev, - mountpoint => $mp, - fstype => $fstype, - lvm => $lvm, - }; - } - - return @dev; -} - -sub get_configs(@) { - local $_; - my %r = (); - foreach (grep {-f} map { (-d) ? glob("$_/*") : $_ } @_) { - - # check permission and ownership - { - my $p = (stat)[2] & 07777; - my $u = (stat _)[4]; - die "$ME: $_ has wrong permissions: found @{[sprintf '%04o', $p]}, need 0600\n" - if $p != 0600; - die "$ME: owner of $_ ($u) is not the EUID ($EUID) of this process\n" - if (stat _)[4] != $EUID; - - # FIXME: should check the containing directories too! - }; - - my $f = new IO::File $_ or die "Can't open $_: $!\n"; - my %h = map { split /\s*=\s*/, $_, 2 } grep {!/^\s*#/ and /=/} <$f>; - map { chomp } values %h; - %r = (%r, %h); - } - return %r; -} - -{ package FTP; - use strict; - use warnings; - use base qw(Net::FTP); - - sub new { - my $class = shift; - return bless Net::FTP->new(@_) => $class; - } - - sub try { - my $self = shift; - my $func = shift; - $self->$func(@_) - or die "FTP $func failed: " . $self->message . "\n"; - } - - sub mkpath { - my $self = shift; - my $current = $self->pwd(); - foreach (split /\/+/, $_[0]) { - next if $self->cwd($_); - return undef if not $self->message ~~ /no such .*dir/i; - return undef if not $self->SUPER::mkdir($_); - return undef if not $self->cwd($_); - } - $self->cwd($current); - } -} - -__END__ - -=head1 NAME - -py2b - backup tool - -=head1 SYNOPSIS - - py2b [--level ] [options] - -=head1 OPTIONS - -=over - -=item B<-d>|B<--debug> [I] - -Enables debugging for the specified items (comma separated). -If no item is specified, just some debugging is done. - -Valid items are B and currently nothing else. - -Even more debugging is shown using the DEBUG=1 environment setting. - -=item B<-f>|B<--force> - -Use more power (e.g. overwrite a previous level backup and remove all -invalidated other backups). (default: 0) - -=item B<-l>|B<--level> I - -The backup level. Level other than "0" needs a previous -level 0 (full) backup. (default: 0) - -=item B<-v>|B<--verbose> - -Be verbose. (default: no) - -=back - -=head1 FILES - -The config files are searched in the following places: - - /etc/py2b - ~/.py2b - ./py2b.conf - -If the location is a directory, all (not hidden) files in this directory are -considered to be config, if the location a file itself, this is considered to -be a config file. The config files have to be mode 0600 and they have to be -owned by the EUID running the process. - -=cut - -# vim:sts=4 sw=4 aw ai sm: diff -r c3c8a413c020 -r 1691a932eed1 py2b.conf.example --- a/py2b.conf.example Mon Oct 26 23:35:18 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -# example config -# the commented values are the built in defaults - -# The encryption key -KEY = - -# FTP-Server hostname -FTP_HOST = - -# FTP-Server base directory -# the following expansion work: -# $NODE -# FTP_DIR = backups/daily/$NODE - -# if we need passive mode for file transfer -# FTP_PASSIVE = 1