X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/9b3318b0df79ce6e9490c744ef7ae0707bd91599..25ff04417e4b4fa0aeb44e0b1576a01021aa1650:/main.c diff --git a/main.c b/main.c index e0a90455..8ba45a26 100644 --- a/main.c +++ b/main.c @@ -33,8 +33,9 @@ extern int verbose; extern int blocking_io; extern int cvs_exclude; extern int delete_mode; -extern int delete_excluded; +extern int delete_before; extern int delete_after; +extern int delete_excluded; extern int daemon_over_rsh; extern int do_stats; extern int dry_run; @@ -43,6 +44,7 @@ extern int local_server; extern int log_got_error; extern int module_id; extern int orig_umask; +extern int copy_links; extern int keep_dirlinks; extern int preserve_hard_links; extern int protocol_version; @@ -53,10 +55,13 @@ extern int whole_file; extern int read_batch; extern int write_batch; extern int batch_fd; +extern int batch_gen_fd; extern int filesfrom_fd; extern pid_t cleanup_child_pid; extern char *files_from; extern char *remote_filesfrom_file; +extern char *partial_dir; +extern char *basis_dir[]; extern char *rsync_path; extern char *shell_cmd; extern char *batch_name; @@ -143,6 +148,10 @@ static void report(int f) write_longint(f, total_read); write_longint(f, total_written); write_longint(f, stats.total_size); + if (protocol_version >= 29) { + write_longint(f, stats.flist_buildtime); + write_longint(f, stats.flist_xfertime); + } } return; } @@ -150,15 +159,25 @@ static void report(int f) /* this is the client */ if (!am_sender) { + /* Read the first two in opposite order because the meaning of + * read/write swaps when switching from sender to receiver. */ total_written = read_longint(f); total_read = read_longint(f); stats.total_size = read_longint(f); + if (protocol_version >= 29) { + stats.flist_buildtime = read_longint(f); + stats.flist_xfertime = read_longint(f); + } } else if (write_batch) { /* The --read-batch process is going to be a client * receiver, so we need to give it the stats. */ write_longint(batch_fd, total_read); write_longint(batch_fd, total_written); write_longint(batch_fd, stats.total_size); + if (protocol_version >= 29) { + write_longint(batch_fd, stats.flist_buildtime); + write_longint(batch_fd, stats.flist_xfertime); + } } if (do_stats) { @@ -174,15 +193,23 @@ static void report(int f) rprintf(FINFO,"Matched data: %.0f bytes\n", (double)stats.matched_data); rprintf(FINFO,"File list size: %d\n", stats.flist_size); - rprintf(FINFO,"Total bytes written: %.0f\n", + if (stats.flist_buildtime) { + rprintf(FINFO, + "File list generation time: %.3f seconds\n", + (double)stats.flist_buildtime / 1000); + rprintf(FINFO, + "File list transfer time: %.3f seconds\n", + (double)stats.flist_xfertime / 1000); + } + rprintf(FINFO,"Total bytes sent: %.0f\n", (double)total_written); - rprintf(FINFO,"Total bytes read: %.0f\n", + rprintf(FINFO,"Total bytes received: %.0f\n", (double)total_read); } if (verbose || do_stats) { rprintf(FINFO, - "\nwrote %.0f bytes read %.0f bytes %.2f bytes/sec\n", + "\nsent %.0f bytes received %.0f bytes %.2f bytes/sec\n", (double)total_written, (double)total_read, (total_written + total_read)/(0.5 + (t - starttime))); rprintf(FINFO, "total size is %.0f speedup is %.2f\n", @@ -200,7 +227,7 @@ static void report(int f) **/ static void show_malloc_stats(void) { -#ifdef HAVE_MALLINFO +#if HAVE_MALLINFO struct mallinfo mi; mi = mallinfo(); @@ -311,15 +338,24 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char *path, rprintf(FINFO,"\n"); } - if (local_server) { + if (read_batch) { + int from_gen_pipe[2]; + if (fd_pair(from_gen_pipe) < 0) { + rsyserr(FERROR, errno, "pipe"); + exit_cleanup(RERR_IPC); + } + batch_gen_fd = from_gen_pipe[0]; + *f_out = from_gen_pipe[1]; + *f_in = batch_fd; + ret = -1; /* no child pid */ + } else if (local_server) { /* If the user didn't request --[no-]whole-file, force * it on, but only if we're not batch processing. */ - if (whole_file < 0 && !read_batch && !write_batch) + if (whole_file < 0 && !write_batch) whole_file = 1; ret = local_child(argc, args, f_in, f_out, child_main); - } else { + } else ret = piped_child(args,f_in,f_out); - } if (dir) free(dir); @@ -366,9 +402,13 @@ static char *get_local_name(struct file_list *flist,char *name) if (do_mkdir(name,0777 & ~orig_umask) != 0) { rsyserr(FERROR, errno, "mkdir %s failed", full_fname(name)); exit_cleanup(RERR_FILEIO); - } else { - if (verbose > 0) - rprintf(FINFO,"created directory %s\n",name); + } + if (verbose > 0) + rprintf(FINFO, "created directory %s\n", name); + + if (dry_run) { + dry_run++; + return NULL; } if (!push_dir(name)) { @@ -392,7 +432,7 @@ static void do_server_sender(int f_in, int f_out, int argc,char *argv[]) (long)getpid()); } - if (am_daemon && lp_write_only(module_id) && am_sender) { + if (am_daemon && lp_write_only(module_id)) { rprintf(FERROR, "ERROR: module is write only\n"); exit_cleanup(RERR_SYNTAX); return; @@ -414,7 +454,7 @@ static void do_server_sender(int f_in, int f_out, int argc,char *argv[]) argv[i] += l+1; } - if (argc == 0 && recurse) { + if (argc == 0 && (recurse || list_only)) { argc = 1; argv--; argv[0] = "."; @@ -425,8 +465,9 @@ static void do_server_sender(int f_in, int f_out, int argc,char *argv[]) exit_cleanup(0); } - io_start_buffering_in(f_in); - io_start_buffering_out(f_out); + io_start_buffering_in(); + io_start_buffering_out(); + send_files(flist,f_out,f_in); io_flush(FULL_FLUSH); report(f_out); @@ -443,37 +484,52 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) { int pid; int status = 0; - int error_pipe[2]; + int error_pipe[2], name_pipe[2]; + BOOL need_name_pipe = (basis_dir[0] || partial_dir) && !dry_run; + + /* The receiving side mustn't obey this, or an existing symlink that + * points to an identical file won't be replaced by the referent. */ + copy_links = 0; if (preserve_hard_links) init_hard_links(flist); - if (!delete_after) { + if (delete_before) { /* I moved this here from recv_files() to prevent a race condition */ - if (recurse && delete_mode && !local_name && flist->count>0) { + if (recurse && !local_name && flist->count > 0) delete_files(flist); - } } - if (fd_pair(error_pipe) < 0) { - rprintf(FERROR,"error pipe failed in do_recv\n"); - exit_cleanup(RERR_SOCKETIO); + if (fd_pair(error_pipe) < 0 + || (need_name_pipe && fd_pair(name_pipe) < 0)) { + rsyserr(FERROR, errno, "pipe failed in do_recv"); + exit_cleanup(RERR_IPC); } io_flush(NORMAL_FLUSH); - if ((pid = do_fork()) == 0) { + if ((pid = do_fork()) == -1) { + rsyserr(FERROR, errno, "fork failed in do_recv"); + exit_cleanup(RERR_IPC); + } + + if (pid == 0) { close(error_pipe[0]); + if (need_name_pipe) { + close(name_pipe[1]); + set_blocking(name_pipe[0]); + } else + name_pipe[0] = -1; if (f_in != f_out) close(f_out); /* we can't let two processes write to the socket at one time */ - io_multiplexing_close(); + close_multiplexing_out(); /* set place to send errors */ set_msg_fd_out(error_pipe[1]); - recv_files(f_in,flist,local_name); + recv_files(f_in, flist, local_name, name_pipe[0]); io_flush(FULL_FLUSH); report(f_in); @@ -487,20 +543,25 @@ static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) } am_generator = 1; + close_multiplexing_in(); if (write_batch) stop_write_batch(); close(error_pipe[1]); + if (need_name_pipe) { + close(name_pipe[0]); + set_nonblocking(name_pipe[1]); + } else + name_pipe[1] = -1; if (f_in != f_out) close(f_in); - io_start_buffering_out(f_out); + io_start_buffering_out(); set_msg_fd_in(error_pipe[0]); - generate_files(f_out, flist, local_name); + generate_files(f_out, flist, local_name, name_pipe[1]); - get_redo_num(); /* Read final MSG_DONE and any prior messages. */ report(-1); io_flush(FULL_FLUSH); if (protocol_version >= 24) { @@ -522,13 +583,20 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) struct file_list *flist; char *local_name = NULL; char *dir = NULL; + int save_verbose = verbose; + + if (filesfrom_fd >= 0) { + /* We can't mix messages with files-from data on the socket, + * so temporarily turn off verbose messages. */ + verbose = 0; + } if (verbose > 2) { rprintf(FINFO, "server_recv(%d) starting pid=%ld\n", argc, (long)getpid()); } - if (am_daemon && lp_read_only(module_id) && !am_sender) { + if (am_daemon && lp_read_only(module_id)) { rprintf(FERROR,"ERROR: module is read only\n"); exit_cleanup(RERR_SYNTAX); return; @@ -546,20 +614,24 @@ static void do_server_recv(int f_in, int f_out, int argc,char *argv[]) } } - io_start_buffering_in(f_in); + io_start_buffering_in(); if (delete_mode && !delete_excluded) - recv_exclude_list(f_in); + recv_filter_list(f_in); + if (cvs_exclude) + add_cvs_excludes(); if (filesfrom_fd >= 0) { - /* We're receiving the file info from the sender, so we need - * the IO routines to automatically write out the names onto - * our f_out socket as we read the list info from the sender. - * This avoids both deadlock and extra delays/buffers. */ + /* We need to send the files-from names to the sender at the + * same time that we receive the file-list from them, so we + * need the IO routines to automatically write out the names + * onto our f_out socket as we read the file-list. This + * avoids both deadlock and extra delays/buffers. */ io_set_filesfrom_fds(filesfrom_fd, f_out); filesfrom_fd = -1; } flist = recv_file_list(f_in); + verbose = save_verbose; if (!flist) { rprintf(FERROR,"server_recv: recv_file_list error\n"); exit_cleanup(RERR_FILESELECT); @@ -588,18 +660,19 @@ int child_main(int argc, char *argv[]) void start_server(int f_in, int f_out, int argc, char *argv[]) { - setup_protocol(f_out, f_in); - set_nonblocking(f_in); set_nonblocking(f_out); + io_set_sock_fds(f_in, f_out); + setup_protocol(f_out, f_in); + if (protocol_version >= 23) - io_start_multiplex_out(f_out); + io_start_multiplex_out(); if (am_sender) { keep_dirlinks = 0; /* Must be disabled on the sender. */ - recv_exclude_list(f_in); + recv_filter_list(f_in); if (cvs_exclude) add_cvs_excludes(); do_server_sender(f_in, f_out, argc, argv); @@ -621,41 +694,44 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) char *local_name = NULL; cleanup_child_pid = pid; - if (read_batch) { - /* This is the heart of the read_batch approach: - * Switcher-roo the file descriptors, and - * nobody's the wiser. */ - close(f_in); - close(f_out); - f_in = batch_fd; - f_out = do_open("/dev/null", O_WRONLY, 0); - assert(am_sender == 0); - } else { + 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 (protocol_version >= 23 && !read_batch) - io_start_multiplex_in(f_in); + 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(f_out); + io_start_buffering_out(); + if (!remote_filesfrom_file) + set_msg_fd_in(f_in); if (cvs_exclude) add_cvs_excludes(); if (delete_mode && !delete_excluded) - send_exclude_list(f_out); + send_filter_list(f_out); if (remote_filesfrom_file) filesfrom_fd = f_in; if (write_batch) start_write_batch(f_out); - /* Can be unconditional, but this is theoretically - * more efficent for read_batch case. */ if (!read_batch) /* don't write to pipe */ flist = send_file_list(f_out,argc,argv); + set_msg_fd_in(-1); if (verbose > 3) rprintf(FINFO,"file list sent\n"); @@ -678,12 +754,12 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]) } if (argc == 0) - list_only = 1; + list_only |= 1; - /* Can be unconditional, but this is theoretically more - * efficient for the read_batch case. */ if (!read_batch) - send_exclude_list(f_out); + send_filter_list(f_out); + if (cvs_exclude) + add_cvs_excludes(); if (filesfrom_fd >= 0) { io_set_filesfrom_fds(filesfrom_fd, f_out); @@ -762,15 +838,20 @@ static int start_client(int argc, char *argv[]) host = argv[0] + strlen(URL_PREFIX); p = strchr(host,'/'); if (p) { - *p = 0; + *p = '\0'; path = p+1; - } else { + } else path = ""; - } - p = strchr(host,':'); + if (*host == '[' && (p = strchr(host, ']')) != NULL) { + host++; + *p++ = '\0'; + if (*p != ':') + p = NULL; + } else + p = strchr(host, ':'); if (p) { rsync_port = atoi(p+1); - *p = 0; + *p = '\0'; } return start_socket_client(host, path, argc-1, argv+1); } @@ -782,7 +863,7 @@ static int start_client(int argc, char *argv[]) && remote_filesfrom_file != files_from + 1 && strncmp(files_from, argv[0], p-argv[0]+1) != 0) { rprintf(FERROR, - "--files-from hostname is not transfer hostname\n"); + "--files-from hostname is not the same as the transfer hostname\n"); exit_cleanup(RERR_SYNTAX); } if (p[1] == ':') { /* double colon */ @@ -815,15 +896,20 @@ static int start_client(int argc, char *argv[]) host = argv[argc-1] + strlen(URL_PREFIX); p = strchr(host,'/'); if (p) { - *p = 0; + *p = '\0'; path = p+1; - } else { + } else path = ""; - } - p = strchr(host,':'); + if (*host == '[' && (p = strchr(host, ']')) != NULL) { + host++; + *p++ = '\0'; + if (*p != ':') + p = NULL; + } else + p = strchr(host, ':'); if (p) { rsync_port = atoi(p+1); - *p = 0; + *p = '\0'; } return start_socket_client(host, path, argc-1, argv); } @@ -833,14 +919,14 @@ static int start_client(int argc, char *argv[]) && remote_filesfrom_file != files_from + 1 && strncmp(files_from, argv[argc-1], p-argv[argc-1]+1) != 0) { rprintf(FERROR, - "--files-from hostname is not transfer hostname\n"); + "--files-from hostname is not the same as the transfer hostname\n"); exit_cleanup(RERR_SYNTAX); } if (!p) { /* no colon found, so src & dest are local */ local_server = 1; if (remote_filesfrom_file) { rprintf(FERROR, - "--files-from is remote but transfer is local\n"); + "--files-from cannot be remote when the transfer is local\n"); exit_cleanup(RERR_SYNTAX); } } else if (p[1] == ':') { /* double colon */ @@ -901,9 +987,8 @@ static int start_client(int argc, char *argv[]) } /* ... or no dest at all */ - if (!am_sender && argc == 0) { - list_only = 1; - } + if (!am_sender && argc == 0) + list_only |= 1; pid = do_cmd(shell_cmd,shell_machine,shell_user,shell_path, &f_in,&f_out); @@ -1017,11 +1102,8 @@ static RETSIGTYPE rsync_panic_handler(UNUSED(int whatsig)) int main(int argc,char *argv[]) { int ret; - int orig_argc; - char **orig_argv; - - orig_argc = argc; - orig_argv = argv; + int orig_argc = argc; + char **orig_argv = argv; signal(SIGUSR1, sigusr1_handler); signal(SIGUSR2, sigusr2_handler); @@ -1072,16 +1154,22 @@ int main(int argc,char *argv[]) if (write_batch || read_batch) { if (write_batch) - write_batch_argvs_file(orig_argc, orig_argv); + write_batch_shell_file(orig_argc, orig_argv, argc); - batch_fd = do_open(batch_name, + if (read_batch && strcmp(batch_name, "-") == 0) + batch_fd = STDIN_FILENO; + else { + batch_fd = do_open(batch_name, write_batch ? O_WRONLY | O_CREAT | O_TRUNC : O_RDONLY, S_IRUSR | S_IWUSR); + } if (batch_fd < 0) { rsyserr(FERROR, errno, "Batch file %s open error", batch_name); exit_cleanup(RERR_FILEIO); } + if (read_batch) + read_stream_flags(batch_fd); } if (am_daemon && !am_server)