A patch to add two new daemon module options:

    pre-xfer exec = COMMAND
    post-xfer exec = COMMAND

The "pre-xfer exec" command runs before the transfer happens, while the
'post-xfer exec" command runs after the transfer completes, even if the
transfer failed.  The following environment variables will be set in
both cases:

    RSYNC_MODULE_NAME  The name of the module being accessed.
    RSYNC_MODULE_PATH  The path configured for the module.
    RSYNC_HOST_ADDR    The accessing host's IP address.
    RSYNC_HOST_NAME    The accessing host's name.
    RSYNC_USER_NAME    The accessing user's name.

These environment variables will also be set for the "post-xfer exec"
command:

    RSYNC_EXIT_STATUS  rsync's exit value.  This will be 0 for a
                       successful run, a positive value for an error
                       that rsync returned (e.g. 23=partial xfer),
                       or a -1 if rsync failed to exit properly.
    RSYNC_RAW_STATUS  the raw exit value from waitpid().

Both commands are run by the user that started the daemon (not the
module's uid/gid setting) without any chroot() restrictions (even if
the module will/has run chroot()ed).

After applying this patch, run these commands for a successful build:

    autoconf
    autoheader
    ./configure
    make proto
    make

--- orig/clientserver.c	2005-07-28 18:48:38
+++ clientserver.c	2005-07-28 10:27:33
@@ -227,6 +227,9 @@ static int rsync_module(int f_in, int f_
 	uid_t uid = (uid_t)-2;  /* canonically "nobody" */
 	gid_t gid = (gid_t)-2;
 	char *p;
+#ifdef HAVE_PUTENV
+	char *s;
+#endif
 	char *addr = client_addr(f_in);
 	char *host = client_name(f_in);
 	char *name = lp_name(i);
@@ -347,6 +350,58 @@ static int rsync_module(int f_in, int f_
 
 	log_init();
 
+#ifdef HAVE_PUTENV
+	s = lp_prexfer_exec(i);
+	p = lp_postxfer_exec(i);
+	if ((s && *s) || (p && *p)) {
+		char *modname, *modpath, *hostaddr, *hostname, *username;
+		int status;
+		if (asprintf(&modname, "RSYNC_MODULE_NAME=%s", name) < 0
+		 || asprintf(&modpath, "RSYNC_MODULE_PATH=%s", lp_path(i)) < 0
+		 || asprintf(&hostaddr, "RSYNC_HOST_ADDR=%s", addr) < 0
+		 || asprintf(&hostname, "RSYNC_HOST_NAME=%s", host) < 0
+		 || asprintf(&username, "RSYNC_USER_NAME=%s", auth_user) < 0)
+			out_of_memory("rsync_module");
+		putenv(modname);
+		putenv(modpath);
+		putenv(hostaddr);
+		putenv(hostname);
+		putenv(username);
+		umask(orig_umask);
+		if (s && *s) {
+			status = system(s);
+			if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+				rprintf(FLOG, "prexfer-exec failed\n");
+				io_printf(f_out, "@ERROR: prexfer-exec failed\n");
+				return -1;
+			}
+		}
+		if (p && *p) {
+			pid_t pid = fork();
+			if (pid < 0) {
+				rsyserr(FLOG, errno, "fork failed");
+				io_printf(f_out, "@ERROR: fork failed\n");
+				return -1;
+			}
+			if (pid) {
+				char *ret1, *ret2;
+				waitpid(pid, &status, 0);
+				if (asprintf(&ret1, "RSYNC_RAW_STATUS=%d", status) > 0)
+					putenv(ret1);
+				if (WIFEXITED(status))
+					status = WEXITSTATUS(status);
+				else
+					status = -1;
+				if (asprintf(&ret2, "RSYNC_EXIT_STATUS=%d", status) > 0)
+					putenv(ret2);
+				system(p);
+				_exit(0);
+			}
+		}
+		umask(0);
+	}
+#endif
+
 	if (use_chroot) {
 		/*
 		 * XXX: The 'use chroot' flag is a fairly reliable
--- orig/configure.in	2005-07-28 18:48:38
+++ configure.in	2005-07-28 19:08:54
@@ -500,7 +500,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd strd
     fchmod fstat strchr readlink link utime utimes strftime mtrace ftruncate \
     memmove lchown vsnprintf snprintf vasprintf asprintf setsid glob strpbrk \
     strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
-    setlocale setmode open64 lseek64 mkstemp64 va_copy __va_copy)
+    setlocale setmode open64 lseek64 mkstemp64 va_copy __va_copy putenv)
 
 AC_CHECK_FUNCS(getpgrp tcgetpgrp)
 if test $ac_cv_func_getpgrp = yes; then
--- orig/loadparm.c	2005-07-28 18:48:38
+++ loadparm.c	2005-07-28 10:27:34
@@ -140,6 +140,8 @@ typedef struct
 	char *log_format;
 	char *refuse_options;
 	char *dont_compress;
+	char *prexfer_exec;
+	char *postxfer_exec;
 	int timeout;
 	int max_connections;
 	int max_verbosity;
@@ -175,6 +177,8 @@ static service sDefault =
 	"%o %h [%a] %m (%u) %f %l",    /* log format */
 	NULL,    /* refuse options */
 	"*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz",    /* dont compress */
+	NULL,     /* prexfer_exec */
+	NULL,     /* postxfer_exec */
 	0,        /* timeout */
 	0,        /* max connections */
 	1,        /* max verbosity */
@@ -298,6 +302,10 @@ static struct parm_struct parm_table[] =
   {"log format",       P_STRING,  P_LOCAL,  &sDefault.log_format,  NULL,   0},
   {"refuse options",   P_STRING,  P_LOCAL,  &sDefault.refuse_options,NULL, 0},
   {"dont compress",    P_STRING,  P_LOCAL,  &sDefault.dont_compress,NULL,  0},
+#ifdef HAVE_PUTENV
+  {"pre-xfer exec",    P_STRING,  P_LOCAL,  &sDefault.prexfer_exec, NULL,  0},
+  {"post-xfer exec",   P_STRING,  P_LOCAL,  &sDefault.postxfer_exec,NULL,  0},
+#endif
   {NULL,               P_BOOL,    P_NONE,   NULL,                  NULL,   0}
 };
 
@@ -379,6 +387,8 @@ FN_LOCAL_STRING(lp_include_from, include
 FN_LOCAL_STRING(lp_log_format, log_format)
 FN_LOCAL_STRING(lp_refuse_options, refuse_options)
 FN_LOCAL_STRING(lp_dont_compress, dont_compress)
+FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
+FN_LOCAL_STRING(lp_postxfer_exec, postxfer_exec)
 FN_LOCAL_INTEGER(lp_timeout, timeout)
 FN_LOCAL_INTEGER(lp_max_connections, max_connections)
 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
--- orig/rsyncd.conf.yo	2005-07-28 19:26:48
+++ rsyncd.conf.yo	2005-07-28 10:27:35
@@ -454,6 +454,35 @@ of the patterns will not be compressed d
 
 The default setting is tt(*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz)
 
+dit(bf(pre-xfer exec), bf(post-xfer exec)) You may specify a command to be run
+before and/or after the transfer.  If the bf(pre-xfer exec) command fails, the
+transfer is aborted before it begins.
+
+The following environment variables are set for both commands:
+
+quote(itemize(
+  it() bf(RSYNC_MODULE_NAME): The name of the module being accessed.
+  it() bf(RSYNC_MODULE_PATH): The path configured for the module.
+  it() bf(RSYNC_HOST_ADDR): The accessing host's IP address.
+  it() bf(RSYNC_HOST_NAME): The accessing host's name.
+  it() bf(RSYNC_USER_NAME): The accessing user's name (empty if no user).
+))
+
+These environment variables will also be set for the bf(post-xfer exec)
+command:
+
+quote(itemize(
+  it() bf(RSYNC_EXIT_STATUS): rsync's exit value.  This will be 0 for a
+  successful run, a positive value for an error that rsync returned
+  (e.g. 23=partial xfer), or a -1 if rsync failed to exit properly.
+  it() bf(RSYNC_RAW_STATUS): the raw exit value from waitpid().
+))
+
+Even though the commands can be associated with a particular module, they
+are run using the permissions of the user that started the daemon (not the
+module's uid/gid setting) without any chroot restrictions (even if the
+module will/has run chroot()ed).
+
 enddit()
 
 manpagesection(AUTHENTICATION STRENGTH)
