-enum {OPT_VERSION,OPT_SUFFIX,OPT_SENDER,OPT_SERVER,OPT_EXCLUDE,
- OPT_EXCLUDE_FROM,OPT_DELETE,OPT_RSYNC_PATH};
-
-static char *short_options = "oblHpguDCtcahvrIxnSe:B:z";
-
-static struct option long_options[] = {
- {"version", 0, 0, OPT_VERSION},
- {"server", 0, 0, OPT_SERVER},
- {"sender", 0, 0, OPT_SENDER},
- {"delete", 0, 0, OPT_DELETE},
- {"exclude", 1, 0, OPT_EXCLUDE},
- {"exclude-from",1, 0, OPT_EXCLUDE_FROM},
- {"rsync-path", 1, 0, OPT_RSYNC_PATH},
- {"one-file-system",0, 0, 'x'},
- {"ignore-times",0, 0, 'I'},
- {"help", 0, 0, 'h'},
- {"dry-run", 0, 0, 'n'},
- {"sparse", 0, 0, 'S'},
- {"cvs-exclude", 0, 0, 'C'},
- {"archive", 0, 0, 'a'},
- {"checksum", 0, 0, 'c'},
- {"backup", 0, 0, 'b'},
- {"update", 0, 0, 'u'},
- {"verbose", 0, 0, 'v'},
- {"recursive", 0, 0, 'r'},
- {"devices", 0, 0, 'D'},
- {"perms", 0, 0, 'p'},
- {"links", 0, 0, 'l'},
- {"hard-links", 0, 0, 'H'},
- {"owner", 0, 0, 'o'},
- {"group", 0, 0, 'g'},
- {"times", 0, 0, 't'},
- {"rsh", 1, 0, 'e'},
- {"suffix", 1, 0, OPT_SUFFIX},
- {"block-size", 1, 0, 'B'},
- {"compress", 0, 0, 'z'},
- {0,0,0,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 */
+ argc--;
+ 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) { /* destination required */
+ usage(FERROR);
+ exit_cleanup(RERR_SYNTAX);
+ }
+
+ shell_path = check_for_hostspec(argv[argc], &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 = argv[argc];
+ } 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))
+{
+ exit_cleanup(RERR_SIGNAL1);
+}
+
+static RETSIGTYPE sigusr2_handler(UNUSED(int val))
+{
+ if (!am_server)
+ output_summary();
+ close_all();
+ if (log_got_error)
+ _exit(RERR_PARTIAL);
+ _exit(0);
+}
+
+static RETSIGTYPE sigchld_handler(UNUSED(int val))
+{
+#ifdef WNOHANG
+ int cnt, status;
+ pid_t pid;
+ /* An empty waitpid() loop was put here by Tridge and we could never
+ * get him to explain why he put it in, so rather than taking it
+ * out we're instead saving the child exit statuses for later use.
+ * The waitpid() loop presumably eliminates all possibility of leaving
+ * zombie children, maybe that's why he did it. */
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ /* save the child's exit status */
+ for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) {
+ if (pid_stat_table[cnt].pid == 0) {
+ pid_stat_table[cnt].pid = pid;
+ pid_stat_table[cnt].status = status;
+ break;
+ }
+ }
+ }
+#endif
+#ifndef HAVE_SIGACTION
+ signal(SIGCHLD, sigchld_handler);
+#endif
+}
+
+
+/**
+ * This routine catches signals and tries to send them to gdb.
+ *
+ * Because it's called from inside a signal handler it ought not to
+ * use too many library routines.
+ *
+ * @todo Perhaps use "screen -X" instead/as well, to help people
+ * debugging without easy access to X. Perhaps use an environment
+ * variable, or just call a script?
+ *
+ * @todo The /proc/ magic probably only works on Linux (and
+ * Solaris?) Can we be more portable?
+ **/
+#ifdef MAINTAINER_MODE
+const char *get_panic_action(void)
+{
+ const char *cmd_fmt = getenv("RSYNC_PANIC_ACTION");
+
+ if (cmd_fmt)
+ return cmd_fmt;
+ else
+ return "xterm -display :0 -T Panic -n Panic "
+ "-e gdb /proc/%d/exe %d";
+}
+
+
+/**
+ * Handle a fatal signal by launching a debugger, controlled by $RSYNC_PANIC_ACTION.
+ *
+ * This signal handler is only installed if we were configured with
+ * --enable-maintainer-mode. Perhaps it should always be on and we
+ * should just look at the environment variable, but I'm a bit leery
+ * of a signal sending us into a busy loop.
+ **/
+static RETSIGTYPE rsync_panic_handler(UNUSED(int whatsig))
+{
+ char cmd_buf[300];
+ int ret;
+
+ sprintf(cmd_buf, get_panic_action(),
+ getpid(), getpid());
+
+ /* Unless we failed to execute gdb, we allow the process to
+ * continue. I'm not sure if that's right. */
+ ret = system(cmd_buf);
+ if (ret)
+ _exit(ret);
+}
+#endif
+