|
From: <di...@us...> - 2009-03-14 21:14:11
|
Revision: 633
http://safekeep.svn.sourceforge.net/safekeep/?rev=633&view=rev
Author: dimi
Date: 2009-03-14 21:14:03 +0000 (Sat, 14 Mar 2009)
Log Message:
-----------
Frank Crawford <fr...@cr...>
Implements the discussion of invoking an external script
on the client side, during a backup being run.
Modified Paths:
--------------
safekeep/trunk/debian/safekeep-common.docs
safekeep/trunk/doc/safekeep.backup.txt
safekeep/trunk/safekeep
safekeep/trunk/safekeep.spec.in
safekeep/trunk/sample.backup
Added Paths:
-----------
safekeep/trunk/doc/client-script-sample.sh
Modified: safekeep/trunk/debian/safekeep-common.docs
===================================================================
--- safekeep/trunk/debian/safekeep-common.docs 2009-03-01 06:17:14 UTC (rev 632)
+++ safekeep/trunk/debian/safekeep-common.docs 2009-03-14 21:14:03 UTC (rev 633)
@@ -3,3 +3,4 @@
LICENSE
README
TODO
+client-script-sample.sh
Added: safekeep/trunk/doc/client-script-sample.sh
===================================================================
--- safekeep/trunk/doc/client-script-sample.sh (rev 0)
+++ safekeep/trunk/doc/client-script-sample.sh 2009-03-14 21:14:03 UTC (rev 633)
@@ -0,0 +1,19 @@
+#! /bin/sh
+#
+# Safekeep client script
+# API: $1 = Step, $2 = Safekeep ID, $3 = Backup Root Directory
+#
+# Sample script, please configure as appropriate for your site.
+#
+# Note: output from this script is normally only seen in debug mode.
+#
+
+case $1 in
+'STARTUP') mail -s "Safekeep Backup: Started $2" root < /dev/null ;;
+'PRE-SETUP') ;;
+'POST-SETUP') /etc/init.d/autofs condrestart 2>&1 | mail -s "Safekeep Backup: $1 $2" root ;;
+'POST-BACKUP') /etc/init.d/autofs condrestart 2>&1 | mail -s "Safekeep Backup: $1 $2" root ;;
+'POST-SCRUB') ;;
+esac
+
+exit 0
Property changes on: safekeep/trunk/doc/client-script-sample.sh
___________________________________________________________________
Added: svn:executable
+ *
Modified: safekeep/trunk/doc/safekeep.backup.txt
===================================================================
--- safekeep/trunk/doc/safekeep.backup.txt 2009-03-01 06:17:14 UTC (rev 632)
+++ safekeep/trunk/doc/safekeep.backup.txt 2009-03-14 21:14:03 UTC (rev 633)
@@ -64,6 +64,13 @@
size="500M"
/>
+ <!-- location of a script to be executed on the client at different
+ stages of the run. It is called with three arguments:
+ the step of the backup, backup id and the backup root directory -->
+ <script
+ path="/path/to/script"
+ />
+
</setup>
<!-- data to be backup -->
@@ -250,6 +257,16 @@
of the original device's size.
Mandatory for a `<snapshot>` element.
+/backup/setup/script/@path::
+ Execute the script specified path on the client at certain steps
+ of the backup process.
+ This script is executed with three arguments:
+ - Backup id (/backup/@id)
+ - Backup step
+ - Backup root directory (valid after creation of a snapshot)
+ See the `CLIENT SCRIPT` section for more information.
+ Mandatory for a `<script>` element.
+
/backup/data/exclude/@path::
Exclude the file or files matched by the path.
If a directory is matched, then files under that directory will also
@@ -306,7 +323,47 @@
For more information on file selection semantics, please see
`rdiff-backup(1)`.
+CLIENT SCRIPT
+-------------
+`safekeep(1)` support the optional execution of a script or program
+on the client system at different steps during execution of the backup.
+Note: specification of a script which does not exist is not considered an
+error, and is treated as the same as not specifying a script. However, if
+the specified path does match a file or directory, the security tests listed
+below will occur.
+
+This script is executed with the following three arguments:
+- Safekeep step
+- Backup id
+- Backup root directory, which may be set during the creation of a snapshot.
+
+The steps currently defined and tokens passed, are:
+- STARTUP - prior to any execution, however, if it exits with a non-zero
+status this constitues an error and the backup is aborted.
+- PRE-SETUP - prior to running any setup steps being run. A non-zero status
+is considered a warning and execution continues.
+- POST-SETUP - after setup, but prior to execution of backup. A non-zero
+status is considered a warning, and execution continues.
+- POST-BACKUP - after execution of backup. A non-zero status is considered
+a warning, and execution continues.
+- POST-SCRUB - after execution of a server cleanup step, normally only seen
+after a backup failure. A non-zero status is considered a warning, and
+execution continues.
+
+Due to security considerations, there are a number of checks made on this
+script prior to execution and failure of any of these steps will cause the
+backup for that client to be aborted. The following tests are applied prior
+to each execution of the script (i.e. multiple time per backup), in order:
+- script is a regular file, not a directory or special file,
+- script is executable by the user running on the client system,
+- script is owned by root or the user running on the client system,
+- script is NOT writable by any one except the script owner.
+
+Note: no test is made on the ownership of the parent directory or any other
+directories.
+
+
FILES
-----
/etc/safekeep/backup.d/
Modified: safekeep/trunk/safekeep
===================================================================
--- safekeep/trunk/safekeep 2009-03-01 06:17:14 UTC (rev 632)
+++ safekeep/trunk/safekeep 2009-03-14 21:14:03 UTC (rev 633)
@@ -16,7 +16,7 @@
# along with Safekeep. If not, see <http://www.gnu.org/licenses/>.
from __future__ import generators
-import getopt, os, os.path, popen2, re, sys, fnmatch
+import getopt, os, os.path, popen2, re, sys, fnmatch, stat
import commands, tempfile, time, traceback
import getpass, pwd, xml.dom.minidom
import socket, smtplib
@@ -325,6 +325,7 @@
setup_el = backup_el.getElementsByTagName('setup')
dumps = []
snaps = []
+ script = None
if len(setup_el) > 0:
dump_els = setup_el[0].getElementsByTagName('dump')
for dump_el in dump_els:
@@ -332,6 +333,11 @@
snap_els = setup_el[0].getElementsByTagName('snapshot')
for snap_el in snap_els:
snaps.append(parse_snap(snap_el))
+ script_el = setup_el[0].getElementsByTagName('script')
+ if len(script_el) == 1:
+ script = script_el[0].getAttribute('path')
+ elif len(script_el) > 1:
+ raise ConfigException('Can not have more than one setup script element')
data_el = backup_el.getElementsByTagName('data')
@@ -353,7 +359,7 @@
cludes = [{ 'type' : 'exclude', 'path' : path, 'glob' : None, 'regexp' : None } for path in path_xcludes]
return { 'id': id, 'host' : host, 'user' : user, 'key_ctrl' : key_ctrl, 'key_data' : key_data,
- 'dir' : dir, 'retention' : retention, 'dumps' : dumps, 'snaps' : snaps,
+ 'dir' : dir, 'retention' : retention, 'dumps' : dumps, 'snaps' : snaps, 'script' : script,
'cludes' : cludes, 'options' : options, 'bw': bw}
def parse_locs(cfglocs):
@@ -394,11 +400,43 @@
return cfgs
######################################################################
-# DB and SNAPSHOT support
+# Script, DB and SNAPSHOT support
# setup methods can raise exception to signal errors
# teardown methods must succeed and cleanup the state
######################################################################
+def check_script_permissions(script):
+ if not os.path.isfile(script):
+ return '%s is not a regular file' % script
+ if not os.access(script, os.X_OK):
+ return '%s is not executable' % script
+
+ statinfo = os.stat(script)
+ if statinfo.st_uid and statinfo.st_uid != os.getuid():
+ return '%s is owned by others' % script
+
+ if (statinfo.st_mode & (stat.S_IWGRP | stat.S_IWOTH)):
+ return '%s is writable by others' % script
+
+ return None
+
+def client_side_script(step, cfg, bdir):
+ debug('Do client_side_script: step %s' % step)
+
+ ret = None
+ script = cfg['script']
+
+ if script:
+ debug('client_side_script: script = %s' % script)
+ if os.path.exists(script):
+ ret = check_script_permissions(script)
+ if not ret:
+ ret = spawn([script, step, cfg['id'], bdir])
+ else:
+ debug('client_side_script: %s not found' % script)
+
+ return ret
+
def do_client_dbdump(cfg):
debug('Doing DB dumps')
for dump in cfg['dumps']:
@@ -715,21 +753,29 @@
try:
line = sys.stdin.readline()
if line.startswith('ALOHA'):
- do_client_compat(line.split(':', 1)[1])
+ do_client_compat(line.strip().split(':', 1)[1])
send('OK %s, %s' % (PROTOCOL, VERSION))
elif line.startswith('CONFIG'):
cfg = do_client_config(line)
- send('OK')
+ ret = client_side_script('STARTUP', cfg, bdir)
+ if ret:
+ send('ERROR Client-side setup script failed: %s' % ret)
+ else:
+ send('OK')
elif line.startswith('SETUP'):
+ client_side_script('PRE-SETUP', cfg, bdir)
bdir = do_client_setup(cfg)
+ client_side_script('POST-SETUP', cfg, bdir)
send('OK ' + bdir)
elif line.startswith('CLEANUP'):
dir = line[7:].strip()
if dir == bdir: should_cleanup = False
do_client_cleanup(cfg, dir)
+ client_side_script('POST-BACKUP', cfg, bdir)
send('OK')
elif line.startswith('SCRUB'):
do_client_scrub()
+ client_side_script('POST-SCRUB', cfg, bdir)
send('OK')
elif not line:
break
@@ -1362,8 +1408,8 @@
try:
global is_client, verbosity_level, verbosity_ssh, verbosity_trickle
- if verbosity > 0:
- verbosity_trickle = verbosity_ssh = '-' + verbosity * 'v'
+ if verbosity > 2:
+ verbosity_trickle = verbosity_ssh = '-' + (verbosity-2) * 'v'
if mode is 'server':
is_client = False
verbosity_level = 1 + verbosity
Modified: safekeep/trunk/safekeep.spec.in
===================================================================
--- safekeep/trunk/safekeep.spec.in 2009-03-01 06:17:14 UTC (rev 632)
+++ safekeep/trunk/safekeep.spec.in 2009-03-14 21:14:03 UTC (rev 633)
@@ -98,7 +98,7 @@
%defattr(-,root,root,-)
%{_bindir}/safekeep
%{_mandir}/man1/safekeep.1*
-%doc AUTHORS COPYING LICENSE README TODO
+%doc AUTHORS COPYING LICENSE README TODO client-script-sample.sh
%files client
%defattr(-,root,root,-)
Modified: safekeep/trunk/sample.backup
===================================================================
--- safekeep/trunk/sample.backup 2009-03-01 06:17:14 UTC (rev 632)
+++ safekeep/trunk/sample.backup 2009-03-14 21:14:03 UTC (rev 633)
@@ -37,6 +37,12 @@
size="500M"
/>
+ <!-- location of a script to be executed on the client at different
+ stages of the run. It is called with three arguments:
+ the step of the backup, backup id and the backup root directory -->
+ <script
+ path="/path/to/script"
+ />
</setup>
<!-- data to be backuped -->
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|