+
+/*
+ * This is called once the connection has been negotiated. It is used
+ * for rsyncd, remote-shell, and local connections.
+ */
+int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
+{
+ struct file_list *flist = NULL;
+ int exit_code = 0, exit_code2 = 0;
+ char *local_name = NULL;
+
+ cleanup_child_pid = pid;
+ if (!read_batch) {
+ set_nonblocking(f_in);
+ set_nonblocking(f_out);
+ }
+
+ io_set_sock_fds(f_in, f_out);
+ setup_protocol(f_out,f_in);
+#if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
+ setup_iconv();
+#endif
+
+ if (protocol_version >= 23 && !read_batch)
+ io_start_multiplex_in();
+
+ /* We set our stderr file handle to blocking because ssh might have
+ * set it to non-blocking. This can be particularly troublesome if
+ * stderr is a clone of stdout, because ssh would have set our stdout
+ * to non-blocking at the same time (which can easily cause us to lose
+ * output from our print statements). This kluge shouldn't cause ssh
+ * any problems for how we use it. Note also that we delayed setting
+ * this until after the above protocol setup so that we know for sure
+ * that ssh is done twiddling its file descriptors. */
+ set_blocking(STDERR_FILENO);
+
+ if (am_sender) {
+ keep_dirlinks = 0; /* Must be disabled on the sender. */
+ io_start_buffering_out();
+ if (!filesfrom_host)
+ set_msg_fd_in(f_in);
+ send_filter_list(f_out);
+ if (filesfrom_host)
+ filesfrom_fd = f_in;
+
+ if (write_batch && !am_server)
+ start_write_batch(f_out);
+ flist = send_file_list(f_out, argc, argv);
+ set_msg_fd_in(-1);
+ if (verbose > 3)
+ rprintf(FINFO,"file list sent\n");
+ the_file_list = flist;
+
+ io_flush(NORMAL_FLUSH);
+ send_files(flist,f_out,f_in);
+ io_flush(FULL_FLUSH);
+ handle_stats(-1);
+ if (protocol_version >= 24)
+ read_final_goodbye(f_in, f_out);
+ if (pid != -1) {
+ if (verbose > 3)
+ rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
+ io_flush(FULL_FLUSH);
+ wait_process_with_flush(pid, &exit_code);
+ }
+ output_summary();
+ io_flush(FULL_FLUSH);
+ exit_cleanup(exit_code);
+ }
+
+ if (need_messages_from_generator && !read_batch)
+ io_start_multiplex_out();
+
+ if (argc == 0)
+ list_only |= 1;
+
+ send_filter_list(read_batch ? -1 : f_out);
+
+ if (filesfrom_fd >= 0) {
+ io_set_filesfrom_fds(filesfrom_fd, f_out);
+ filesfrom_fd = -1;
+ }
+
+ if (write_batch && !am_server)
+ start_write_batch(f_in);
+ flist = recv_file_list(f_in);
+ the_file_list = flist;
+
+ if (flist && flist->count > 0) {
+ local_name = get_local_name(flist, argv[0]);
+
+ exit_code2 = do_recv(f_in, f_out, flist, local_name);
+ } else {
+ handle_stats(-1);
+ output_summary();
+ }
+
+ if (pid != -1) {
+ if (verbose > 3)
+ rprintf(FINFO,"client_run2 waiting on %d\n", (int) pid);
+ io_flush(FULL_FLUSH);
+ wait_process_with_flush(pid, &exit_code);
+ }
+
+ return MAX(exit_code, exit_code2);
+}
+
+static int copy_argv (char *argv[])
+{
+ int i;
+
+ for (i = 0; argv[i]; i++) {
+ if (!(argv[i] = strdup(argv[i]))) {
+ rprintf (FERROR, "out of memory at %s(%d)\n",
+ __FILE__, __LINE__);
+ return RERR_MALLOC;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Start a client for either type of remote connection. Work out
+ * whether the arguments request a remote shell or rsyncd connection,
+ * and call the appropriate connection function, then run_client.
+ *
+ * Calls either start_socket_client (for sockets) or do_cmd and
+ * client_run (for ssh).
+ **/
+static int start_client(int argc, char *argv[])
+{
+ char *p;
+ char *shell_machine = NULL;
+ char *shell_path = NULL;
+ char *shell_user = NULL;
+ int ret;
+ pid_t pid;
+ int f_in,f_out;
+ int rc;
+
+ /* Don't clobber argv[] so that ps(1) can still show the right
+ * command line. */
+ if ((rc = copy_argv(argv)))
+ return rc;
+
+ if (!read_batch) { /* for read_batch, NO source is specified */
+ shell_path = check_for_hostspec(argv[0], &shell_machine, &rsync_port);
+ if (shell_path) { /* source is remote */
+ char *dummy1;
+ int dummy2;
+ if (--argc
+ && check_for_hostspec(argv[argc], &dummy1, &dummy2)) {
+ rprintf(FERROR,
+ "The source and destination cannot both be remote.\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ argv++;
+ if (filesfrom_host && *filesfrom_host
+ && strcmp(filesfrom_host, shell_machine) != 0) {
+ rprintf(FERROR,
+ "--files-from hostname is not the same as the transfer hostname\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (rsync_port) {
+ if (!shell_cmd) {
+ return start_socket_client(shell_machine,
+ shell_path,
+ argc, argv);
+ }
+ daemon_over_rsh = 1;
+ }
+
+ am_sender = 0;
+ } else { /* source is local, check dest arg */
+ am_sender = 1;
+
+ if (argc > 1)
+ p = argv[--argc];
+ else {
+ p = ".";
+ list_only = 1;
+ }
+
+ shell_path = check_for_hostspec(p, &shell_machine, &rsync_port);
+ if (shell_path && filesfrom_host && *filesfrom_host
+ && strcmp(filesfrom_host, shell_machine) != 0) {
+ rprintf(FERROR,
+ "--files-from hostname is not the same as the transfer hostname\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ if (!shell_path) { /* no hostspec found, so src & dest are local */
+ local_server = 1;
+ if (filesfrom_host) {
+ rprintf(FERROR,
+ "--files-from cannot be remote when the transfer is local\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ shell_machine = NULL;
+ shell_path = p;
+ } else if (rsync_port) {
+ if (!shell_cmd) {
+ return start_socket_client(shell_machine,
+ shell_path,
+ argc, argv);
+ }
+ daemon_over_rsh = 1;
+ }
+ }
+ } else { /* read_batch */
+ local_server = 1;
+ shell_path = argv[argc-1];
+ if (check_for_hostspec(shell_path, &shell_machine, &rsync_port)) {
+ rprintf(FERROR, "remote destination is not allowed with --read-batch\n");
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+
+ if (shell_machine) {
+ p = strrchr(shell_machine,'@');
+ if (p) {
+ *p = 0;
+ shell_user = shell_machine;
+ shell_machine = p+1;
+ }
+ }
+
+ if (verbose > 3) {
+ rprintf(FINFO,"cmd=%s machine=%s user=%s path=%s\n",
+ shell_cmd ? shell_cmd : "",
+ shell_machine ? shell_machine : "",
+ shell_user ? shell_user : "",
+ shell_path ? shell_path : "");
+ }
+
+ /* for remote source, only single dest arg can remain ... */
+ if (!am_sender && argc > 1) {
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ /* ... or no dest at all */
+ if (!am_sender && argc == 0)
+ list_only |= 1;
+
+ pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path,
+ &f_in,&f_out);
+
+ /* if we're running an rsync server on the remote host over a
+ * remote shell command, we need to do the RSYNCD protocol first */
+ if (daemon_over_rsh) {
+ int tmpret;
+ tmpret = start_inband_exchange(shell_user, shell_path,
+ f_in, f_out, argc);
+ if (tmpret < 0)
+ return tmpret;
+ }
+
+ ret = client_run(f_in, f_out, pid, argc, argv);
+
+ fflush(stdout);
+ fflush(stderr);
+
+ return ret;
+}
+
+
+static RETSIGTYPE sigusr1_handler(UNUSED(int val))