+ char *bp = buf, *eob = buf + limit - 1;
+
+ while (1) {
+ int got = read(fd, bp, 1);
+ if (got != 1) {
+ if (got < 0 && errno == EINTR)
+ continue;
+ return -1;
+ }
+ if (*bp == '\0')
+ break;
+ if (bp < eob)
+ bp++;
+ }
+ *bp = '\0';
+
+ return bp - buf;
+}
+#endif
+
+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;