Enhanced the "pre-xfer exec" handling so that it gets the user's
authorWayne Davison <wayned@samba.org>
Mon, 24 Oct 2005 21:03:50 +0000 (21:03 +0000)
committerWayne Davison <wayned@samba.org>
Mon, 24 Oct 2005 21:03:50 +0000 (21:03 +0000)
request info and puts that into the environment as RSYNC_REQUEST.

clientserver.c

index e069267..c0ace0c 100644 (file)
@@ -59,6 +59,8 @@ int module_id = -1;
 /* Length of lp_path() string when in daemon mode & not chrooted, else 0. */
 unsigned int module_dirlen = 0;
 
+#define MAX_REQ_LEN (MAXPATHLEN + 255)
+
 /**
  * Run a client connected to an rsyncd.  The alternative to this
  * function for remote-shell connections is do_cmd().
@@ -216,7 +218,27 @@ int start_inband_exchange(char *user, char *path, int f_in, int f_out,
        return 0;
 }
 
+static char *finish_pre_exec(pid_t pid, int fd, char *request)
+{
+       int status = -1;
 
+       if (request) {
+               int len = strlen(request);
+               if (len > MAX_REQ_LEN)
+                       len = MAX_REQ_LEN;
+               write(fd, request, len);
+       }
+       close(fd);
+
+       if (wait_process(pid, &status, 0) < 0
+        || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+               char *e;
+               if (asprintf(&e, "pre-xfer exec returned failure (%d)\n", status) < 0)
+                       out_of_memory("finish_pre_exec");
+               return e;
+       }
+       return NULL;
+}
 
 static int rsync_module(int f_in, int f_out, int i)
 {
@@ -226,16 +248,14 @@ static int rsync_module(int f_in, int f_out, int i)
        char line[BIGPATHBUFLEN];
        uid_t uid = (uid_t)-2;  /* canonically "nobody" */
        gid_t gid = (gid_t)-2;
-       char *p;
-#ifdef HAVE_PUTENV
-       char *s;
-#endif
+       char *p, *err_msg = NULL;
        char *addr = client_addr(f_in);
        char *host = client_name(f_in);
        char *name = lp_name(i);
        int use_chroot = lp_use_chroot(i);
        int start_glob = 0;
-       int ret;
+       int ret, pre_exec_fd = -1;
+       pid_t pre_exec_pid = 0;
        char *request = NULL;
 
        if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) {
@@ -351,9 +371,7 @@ static int rsync_module(int f_in, int f_out, int i)
        log_init();
 
 #ifdef HAVE_PUTENV
-       s = lp_prexfer_exec(i);
-       p = lp_postxfer_exec(i);
-       if ((s && *s) || (p && *p)) {
+       if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
                char *modname, *modpath, *hostaddr, *hostname, *username;
                int status;
                if (asprintf(&modname, "RSYNC_MODULE_NAME=%s", name) < 0
@@ -368,15 +386,10 @@ static int rsync_module(int f_in, int f_out, int i)
                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) {
+               /* For post-xfer exec, fork a new process to run the rsync
+                * daemon while this process waits for the exit status and
+                * runs the indicated command at that point. */
+               if (*lp_postxfer_exec(i)) {
                        pid_t pid = fork();
                        if (pid < 0) {
                                rsyserr(FLOG, errno, "fork failed");
@@ -385,7 +398,8 @@ static int rsync_module(int f_in, int f_out, int i)
                        }
                        if (pid) {
                                char *ret1, *ret2;
-                               waitpid(pid, &status, 0);
+                               if (wait_process(pid, &status, 0) < 0)
+                                       status = -1;
                                if (asprintf(&ret1, "RSYNC_RAW_STATUS=%d", status) > 0)
                                        putenv(ret1);
                                if (WIFEXITED(status))
@@ -394,10 +408,44 @@ static int rsync_module(int f_in, int f_out, int i)
                                        status = -1;
                                if (asprintf(&ret2, "RSYNC_EXIT_STATUS=%d", status) > 0)
                                        putenv(ret2);
-                               system(p);
+                               system(lp_postxfer_exec(i));
                                _exit(status);
                        }
                }
+               /* For pre-xfer exec, fork a child process to run the indicated
+                * command, though it first waits for the parent process to
+                * send us the user's request via a pipe. */
+               if (*lp_prexfer_exec(i)) {
+                       int fds[2];
+                       if (pipe(fds) < 0 || (pre_exec_pid = fork()) < 0) {
+                               rsyserr(FLOG, errno, "pre-xfer exec preparation failed");
+                               io_printf(f_out, "@ERROR: pre-xfer exec preparation failed\n");
+                               return -1;
+                       }
+                       if (pre_exec_pid == 0) {
+                               char buf[MAX_REQ_LEN+1];
+                               int len;
+                               close(fds[1]);
+                               set_blocking(fds[0]);
+                               len = read(fds[0], buf, MAX_REQ_LEN);
+                               close(fds[0]);
+                               if (len <= 0)
+                                       _exit(1);
+                               buf[len] = '\0';
+                               if (asprintf(&p, "RSYNC_REQUEST=%s", buf) < 0)
+                                       out_of_memory("rsync_module");
+                               putenv(p);
+                               close(STDIN_FILENO);
+                               close(STDOUT_FILENO);
+                               status = system(lp_prexfer_exec(i));
+                               if (!WIFEXITED(status))
+                                       _exit(1);
+                               _exit(WEXITSTATUS(status));
+                       }
+                       close(fds[0]);
+                       set_blocking(fds[1]);
+                       pre_exec_fd = fds[1];
+               }
                umask(0);
        }
 #endif
@@ -515,15 +563,24 @@ static int rsync_module(int f_in, int f_out, int i)
                                start_glob = 1;
                        break;
                case 1:
+                       if (pre_exec_pid) {
+                               err_msg = finish_pre_exec(pre_exec_pid,
+                                                         pre_exec_fd, p);
+                               pre_exec_pid = 0;
+                       }
                        request = strdup(p);
                        start_glob = 2;
                        /* FALL THROUGH */
                default:
-                       glob_expand(name, &argv, &argc, &maxargs);
+                       if (!err_msg)
+                               glob_expand(name, &argv, &argc, &maxargs);
                        break;
                }
        }
 
+       if (pre_exec_pid)
+               err_msg = finish_pre_exec(pre_exec_pid, pre_exec_fd, request);
+
        verbose = 0; /* future verbosity is controlled by client options */
        ret = parse_arguments(&argc, (const char ***) &argv, 0);
 
@@ -552,7 +609,7 @@ static int rsync_module(int f_in, int f_out, int i)
        if (protocol_version < 23
            && (protocol_version == 22 || am_sender))
                io_start_multiplex_out();
-       else if (!ret) {
+       else if (!ret || err_msg) {
                /* We have to get I/O multiplexing started so that we can
                 * get the error back to the client.  This means getting
                 * the protocol setup finished first in later versions. */
@@ -578,8 +635,11 @@ static int rsync_module(int f_in, int f_out, int i)
                io_start_multiplex_out();
        }
 
-       if (!ret) {
-               option_error();
+       if (!ret || err_msg) {
+               if (err_msg)
+                       rprintf(FERROR, err_msg);
+               else
+                       option_error();
                msleep(400);
                exit_cleanup(RERR_UNSUPPORTED);
        }
@@ -684,7 +744,6 @@ int start_daemon(int f_in, int f_out)
        return rsync_module(f_in, f_out, i);
 }
 
-
 int daemon_main(void)
 {
        char *pid_file;