+static void set_env_str(const char *var, const char *str)
+{
+#ifdef HAVE_SETENV
+ if (setenv(var, str, 1) < 0)
+ out_of_memory("set_env_str");
+#else
+#ifdef HAVE_PUTENV
+ char *mem;
+ if (asprintf(&mem, "%s=%s", var, str) < 0)
+ out_of_memory("set_env_str");
+ putenv(mem);
+#else
+ (void)var;
+ (void)str;
+#endif
+#endif
+}
+
+#if defined HAVE_SETENV || defined HAVE_PUTENV
+
+static void set_envN_str(const char *var, int num, const char *str)
+{
+#ifdef HAVE_SETENV
+ char buf[128];
+ (void)snprintf(buf, sizeof buf, "%s%d", var, num);
+ if (setenv(buf, str, 1) < 0)
+ out_of_memory("set_env_str");
+#else
+#ifdef HAVE_PUTENV
+ char *mem;
+ if (asprintf(&mem, "%s%d=%s", var, num, str) < 0)
+ out_of_memory("set_envN_str");
+ putenv(mem);
+#endif
+#endif
+}
+
+void set_env_num(const char *var, long num)
+{
+#ifdef HAVE_SETENV
+ char val[64];
+ (void)snprintf(val, sizeof val, "%ld", num);
+ if (setenv(var, val, 1) < 0)
+ out_of_memory("set_env_str");
+#else
+#ifdef HAVE_PUTENV
+ char *mem;
+ if (asprintf(&mem, "%s=%ld", var, num) < 0)
+ out_of_memory("set_env_num");
+ putenv(mem);
+#endif
+#endif
+}
+
+/* Used for "early exec", "pre-xfer exec", and the "name converter" script. */
+static pid_t start_pre_exec(const char *cmd, int *arg_fd_ptr, int *error_fd_ptr)
+{
+ int arg_fds[2], error_fds[2], arg_fd;
+ pid_t pid;
+
+ if ((error_fd_ptr && pipe(error_fds) < 0) || pipe(arg_fds) < 0 || (pid = fork()) < 0)
+ return (pid_t)-1;
+
+ if (pid == 0) {
+ char buf[BIGPATHBUFLEN];
+ int j, len, status;
+
+ if (error_fd_ptr) {
+ close(error_fds[0]);
+ set_blocking(error_fds[1]);
+ }
+
+ close(arg_fds[1]);
+ arg_fd = arg_fds[0];
+ set_blocking(arg_fd);
+
+ len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN);
+ if (len <= 0)
+ _exit(1);
+ set_env_str("RSYNC_REQUEST", buf);
+
+ for (j = 0; ; j++) {
+ len = read_arg_from_pipe(arg_fd, buf, BIGPATHBUFLEN);
+ if (len <= 0) {
+ if (!len)
+ break;
+ _exit(1);
+ }
+ set_envN_str("RSYNC_ARG", j, buf);
+ }
+
+ dup2(arg_fd, STDIN_FILENO);
+ close(arg_fd);
+
+ if (error_fd_ptr) {
+ dup2(error_fds[1], STDOUT_FILENO);
+ close(error_fds[1]);
+ }
+
+ status = shell_exec(cmd);
+
+ if (!WIFEXITED(status))
+ _exit(1);
+ _exit(WEXITSTATUS(status));
+ }
+
+ if (error_fd_ptr) {
+ close(error_fds[1]);
+ *error_fd_ptr = error_fds[0];
+ set_blocking(error_fds[0]);
+ }
+
+ close(arg_fds[0]);
+ arg_fd = *arg_fd_ptr = arg_fds[1];
+ set_blocking(arg_fd);
+
+ return pid;
+}
+
+#endif
+
+static void write_pre_exec_args(int write_fd, char *request, char **early_argv, char **argv, int exec_type)
+{
+ int j = 0;
+
+ if (!request)
+ request = "(NONE)";
+
+ write_buf(write_fd, request, strlen(request)+1);
+ if (early_argv) {
+ for ( ; *early_argv; early_argv++)
+ write_buf(write_fd, *early_argv, strlen(*early_argv)+1);
+ j = 1; /* Skip arg0 name in argv. */
+ }
+ if (argv) {
+ for ( ; argv[j]; j++)
+ write_buf(write_fd, argv[j], strlen(argv[j])+1);
+ }
+ write_byte(write_fd, 0);
+
+ if (exec_type == 1 && early_input_len)
+ write_buf(write_fd, early_input, early_input_len);
+
+ if (exec_type != 2) /* the name converter needs this left open */
+ close(write_fd);
+}
+
+static char *finish_pre_exec(const char *desc, pid_t pid, int read_fd)
+{
+ char buf[BIGPATHBUFLEN], *bp, *cr;
+ int j, status = -1, msglen = sizeof buf - 1;
+
+ if (read_fd >= 0) {
+ /* Read the stdout from the program. This it is only displayed
+ * to the user if the script also returns an error status. */
+ for (bp = buf, cr = buf; msglen > 0; msglen -= j) {
+ if ((j = read(read_fd, bp, msglen)) <= 0) {
+ if (j == 0)
+ break;
+ if (errno == EINTR)
+ continue;
+ break; /* Just ignore the read error for now... */
+ }
+ bp[j] = '\0';
+ while (1) {
+ if ((cr = strchr(cr, '\r')) == NULL) {
+ cr = bp + j;
+ break;
+ }
+ if (!cr[1])
+ break; /* wait for more data before we decide what to do */
+ if (cr[1] == '\n') {
+ memmove(cr, cr+1, j - (cr - bp));
+ j--;
+ } else
+ cr++;
+ }
+ bp += j;
+ }
+ *bp = '\0';
+
+ close(read_fd);
+ } else
+ *buf = '\0';
+
+ if (wait_process(pid, &status, 0) < 0
+ || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ char *e;
+ if (asprintf(&e, "%s returned failure (%d)%s%s%s\n%s",
+ desc, status, status < 0 ? ": " : "",
+ status < 0 ? strerror(errno) : "",
+ *buf ? ":" : "", buf) < 0)
+ return "out_of_memory in finish_pre_exec\n";
+ return e;
+ }
+ return NULL;
+}
+