+static char *finish_pre_exec(pid_t pid, int write_fd, int read_fd, char *request,
+ char **early_argv, char **argv)
+{
+ char buf[BIGPATHBUFLEN], *bp;
+ int j = 0, status = -1, msglen = sizeof buf - 1;
+
+ 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. */
+ }
+ for ( ; argv[j]; j++) {
+ write_buf(write_fd, argv[j], strlen(argv[j])+1);
+ if (argv[j][0] == '.' && argv[j][1] == '\0')
+ break;
+ }
+ write_byte(write_fd, 0);
+
+ close(write_fd);
+
+ /* Read the stdout from the pre-xfer exec program. This it is only
+ * displayed to the user if the script also returns an error status. */
+ for (bp = 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;
+ if (j > 1 && bp[-1] == '\n' && bp[-2] == '\r') {
+ bp--;
+ j--;
+ bp[-1] = '\n';
+ }
+ }
+ *bp = '\0';
+
+ close(read_fd);
+
+ if (wait_process(pid, &status, 0) < 0
+ || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ char *e;
+ if (asprintf(&e, "pre-xfer exec returned failure (%d)%s%s%s\n%s",
+ status, status < 0 ? ": " : "",
+ status < 0 ? strerror(errno) : "",
+ *buf ? ":" : "", buf) < 0)
+ return "out_of_memory in finish_pre_exec\n";
+ return e;
+ }
+ return NULL;
+}
+
+#ifdef HAVE_PUTENV
+static int read_arg_from_pipe(int fd, char *buf, int limit)
+{
+ 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 int path_failure(int f_out, const char *dir, BOOL was_chdir)
+{
+ if (was_chdir)
+ rsyserr(FLOG, errno, "chdir %s failed\n", dir);
+ else
+ rprintf(FLOG, "normalize_path(%s) failed\n", dir);
+ io_printf(f_out, "@ERROR: chdir failed\n");
+ return -1;
+}
+
+static int add_a_group(int f_out, const char *gname)
+{
+ gid_t gid;
+ if (!group_to_gid(gname, &gid, True)) {
+ rprintf(FLOG, "Invalid gid %s\n", gname);
+ io_printf(f_out, "@ERROR: invalid gid %s\n", gname);
+ return -1;
+ }
+ if (gid_count == MAX_GID_LIST) {
+ rprintf(FLOG, "Too many groups specified via gid parameter.\n");
+ io_printf(f_out, "@ERROR: too many groups\n");
+ return -1;
+ }
+ gid_list[gid_count++] = gid;
+ return 0;
+}