📄 main.c
字号:
/* * The startup routines, including main(), for rsync. * * Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org> * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org> * Copyright (C) 2003-2008 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */#include "rsync.h"#include "ifuncs.h"#include "io.h"#if defined CONFIG_LOCALE && defined HAVE_LOCALE_H#include <locale.h>#endifextern int verbose;extern int dry_run;extern int list_only;extern int am_root;extern int am_server;extern int am_sender;extern int am_generator;extern int am_daemon;extern int inc_recurse;extern int blocking_io;extern int remove_source_files;extern int need_messages_from_generator;extern int kluge_around_eof;extern int do_stats;extern int got_xfer_error;extern int module_id;extern int copy_links;extern int copy_dirlinks;extern int copy_unsafe_links;extern int keep_dirlinks;extern int preserve_hard_links;extern int protocol_version;extern int file_total;extern int recurse;extern int xfer_dirs;extern int protect_args;extern int relative_paths;extern int sanitize_paths;extern int curr_dir_depth;extern int curr_dir_len;extern int module_id;extern int rsync_port;extern int whole_file;extern int read_batch;extern int write_batch;extern int batch_fd;extern int filesfrom_fd;extern int connect_timeout;extern pid_t cleanup_child_pid;extern unsigned int module_dirlen;extern struct stats stats;extern char *filesfrom_host;extern char *partial_dir;extern char *dest_option;extern char *basis_dir[];extern char *rsync_path;extern char *shell_cmd;extern char *batch_name;extern char *password_file;extern char curr_dir[MAXPATHLEN];extern struct file_list *first_flist;extern struct filter_list_struct daemon_filter_list;uid_t our_uid;int local_server = 0;int daemon_over_rsh = 0;mode_t orig_umask = 0;int batch_gen_fd = -1;/* There's probably never more than at most 2 outstanding child processes, * but set it higher, just in case. */#define MAXCHILDPROCS 7#ifdef HAVE_SIGACTION# ifdef HAVE_SIGPROCMASK# define SIGACTMASK(n,h) SIGACTION(n,h), sigaddset(&sigmask,(n))# else# define SIGACTMASK(n,h) SIGACTION(n,h)# endifstatic struct sigaction sigact;#endifstruct pid_status { pid_t pid; int status;} pid_stat_table[MAXCHILDPROCS];static time_t starttime, endtime;static int64 total_read, total_written;static void show_malloc_stats(void);/* Works like waitpid(), but if we already harvested the child pid in our * remember_children(), we succeed instead of returning an error. */pid_t wait_process(pid_t pid, int *status_ptr, int flags){ pid_t waited_pid; do { waited_pid = waitpid(pid, status_ptr, flags); } while (waited_pid == -1 && errno == EINTR); if (waited_pid == -1 && errno == ECHILD) { /* Status of requested child no longer available: check to * see if it was processed by remember_children(). */ int cnt; for (cnt = 0; cnt < MAXCHILDPROCS; cnt++) { if (pid == pid_stat_table[cnt].pid) { *status_ptr = pid_stat_table[cnt].status; pid_stat_table[cnt].pid = 0; return pid; } } } return waited_pid;}/* Wait for a process to exit, calling io_flush while waiting. */static void wait_process_with_flush(pid_t pid, int *exit_code_ptr){ pid_t waited_pid; int status; while ((waited_pid = wait_process(pid, &status, WNOHANG)) == 0) { msleep(20); io_flush(FULL_FLUSH); } /* TODO: If the child exited on a signal, then log an * appropriate error message. Perhaps we should also accept a * message describing the purpose of the child. Also indicate * this to the caller so that they know something went wrong. */ if (waited_pid < 0) { rsyserr(FERROR, errno, "waitpid"); *exit_code_ptr = RERR_WAITCHILD; } else if (!WIFEXITED(status)) {#ifdef WCOREDUMP if (WCOREDUMP(status)) *exit_code_ptr = RERR_CRASHED; else#endif if (WIFSIGNALED(status)) *exit_code_ptr = RERR_TERMINATED; else *exit_code_ptr = RERR_WAITCHILD; } else *exit_code_ptr = WEXITSTATUS(status);}/* This function gets called from all 3 processes. We want the client side * to actually output the text, but the sender is the only process that has * all the stats we need. So, if we're a client sender, we do the report. * If we're a server sender, we write the stats on the supplied fd. If * we're the client receiver we read the stats from the supplied fd and do * the report. All processes might also generate a set of debug stats, if * the verbose level is high enough (this is the only thing that the * generator process and the server receiver ever do here). */static void handle_stats(int f){ endtime = time(NULL); /* Cache two stats because the read/write code can change it. */ total_read = stats.total_read; total_written = stats.total_written; if (do_stats && verbose > 1) { /* These come out from every process */ show_malloc_stats(); show_flist_stats(); } if (am_generator) return; if (am_daemon) { if (f == -1 || !am_sender) return; } if (am_server) { if (am_sender) { write_varlong30(f, total_read, 3); write_varlong30(f, total_written, 3); write_varlong30(f, stats.total_size, 3); if (protocol_version >= 29) { write_varlong30(f, stats.flist_buildtime, 3); write_varlong30(f, stats.flist_xfertime, 3); } } return; } /* this is the client */ if (f < 0 && !am_sender) /* e.g. when we got an empty file list. */ ; else 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_varlong30(f, 3); total_read = read_varlong30(f, 3); stats.total_size = read_varlong30(f, 3); if (protocol_version >= 29) { stats.flist_buildtime = read_varlong30(f, 3); stats.flist_xfertime = read_varlong30(f, 3); } } else if (write_batch) { /* The --read-batch process is going to be a client * receiver, so we need to give it the stats. */ write_varlong30(batch_fd, total_read, 3); write_varlong30(batch_fd, total_written, 3); write_varlong30(batch_fd, stats.total_size, 3); if (protocol_version >= 29) { write_varlong30(batch_fd, stats.flist_buildtime, 3); write_varlong30(batch_fd, stats.flist_xfertime, 3); } }}static void output_summary(void){ if (do_stats) { rprintf(FCLIENT, "\n"); rprintf(FINFO,"Number of files: %d\n", stats.num_files); rprintf(FINFO,"Number of files transferred: %d\n", stats.num_transferred_files); rprintf(FINFO,"Total file size: %s bytes\n", human_num(stats.total_size)); rprintf(FINFO,"Total transferred file size: %s bytes\n", human_num(stats.total_transferred_size)); rprintf(FINFO,"Literal data: %s bytes\n", human_num(stats.literal_data)); rprintf(FINFO,"Matched data: %s bytes\n", human_num(stats.matched_data)); rprintf(FINFO,"File list size: %s\n", human_num(stats.flist_size)); 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: %s\n", human_num(total_written)); rprintf(FINFO,"Total bytes received: %s\n", human_num(total_read)); } if (verbose || do_stats) { rprintf(FCLIENT, "\n"); rprintf(FINFO, "sent %s bytes received %s bytes %s bytes/sec\n", human_num(total_written), human_num(total_read), human_dnum((total_written + total_read)/(0.5 + (endtime - starttime)), 2)); rprintf(FINFO, "total size is %s speedup is %.2f%s\n", human_num(stats.total_size), (double)stats.total_size / (total_written+total_read), write_batch < 0 ? " (BATCH ONLY)" : dry_run ? " (DRY RUN)" : ""); } fflush(stdout); fflush(stderr);}/** * If our C library can get malloc statistics, then show them to FINFO **/static void show_malloc_stats(void){#ifdef HAVE_MALLINFO struct mallinfo mi; mi = mallinfo(); rprintf(FCLIENT, "\n"); rprintf(FINFO, RSYNC_NAME "[%d] (%s%s%s) heap statistics:\n", getpid(), am_server ? "server " : "", am_daemon ? "daemon " : "", who_am_i()); rprintf(FINFO, " arena: %10ld (bytes from sbrk)\n", (long)mi.arena); rprintf(FINFO, " ordblks: %10ld (chunks not in use)\n", (long)mi.ordblks); rprintf(FINFO, " smblks: %10ld\n", (long)mi.smblks); rprintf(FINFO, " hblks: %10ld (chunks from mmap)\n", (long)mi.hblks); rprintf(FINFO, " hblkhd: %10ld (bytes from mmap)\n", (long)mi.hblkhd); rprintf(FINFO, " allmem: %10ld (bytes from sbrk + mmap)\n", (long)mi.arena + mi.hblkhd); rprintf(FINFO, " usmblks: %10ld\n", (long)mi.usmblks); rprintf(FINFO, " fsmblks: %10ld\n", (long)mi.fsmblks); rprintf(FINFO, " uordblks: %10ld (bytes used)\n", (long)mi.uordblks); rprintf(FINFO, " fordblks: %10ld (bytes free)\n", (long)mi.fordblks); rprintf(FINFO, " keepcost: %10ld (bytes in releasable chunk)\n", (long)mi.keepcost);#endif /* HAVE_MALLINFO */}/* Start the remote shell. cmd may be NULL to use the default. */static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, int remote_argc, int *f_in_p, int *f_out_p){ int i, argc = 0; char *args[MAX_ARGS]; pid_t pid; int dash_l_set = 0; if (!read_batch && !local_server) { char *t, *f, in_quote = '\0'; char *rsh_env = getenv(RSYNC_RSH_ENV); if (!cmd) cmd = rsh_env; if (!cmd) cmd = RSYNC_RSH; cmd = strdup(cmd); /* MEMORY LEAK */ if (!cmd) goto oom; for (t = f = cmd; *f; f++) { if (*f == ' ') continue; /* Comparison leaves rooms for server_options(). */ if (argc >= MAX_ARGS - MAX_SERVER_ARGS) goto arg_overflow; args[argc++] = t; while (*f != ' ' || in_quote) { if (!*f) { if (in_quote) { rprintf(FERROR, "Missing trailing-%c in remote-shell command.\n", in_quote); exit_cleanup(RERR_SYNTAX); } f--; break; } if (*f == '\'' || *f == '"') { if (!in_quote) { in_quote = *f++; continue; } if (*f == in_quote && *++f != in_quote) { in_quote = '\0'; continue; } } *t++ = *f++; } *t++ = '\0'; } /* check to see if we've already been given '-l user' in * the remote-shell command */ for (i = 0; i < argc-1; i++) { if (!strcmp(args[i], "-l") && args[i+1][0] != '-') dash_l_set = 1; }#ifdef HAVE_REMSH /* remsh (on HPUX) takes the arguments the other way around */ args[argc++] = machine; if (user && !(daemon_over_rsh && dash_l_set)) { args[argc++] = "-l"; args[argc++] = user; }#else if (user && !(daemon_over_rsh && dash_l_set)) { args[argc++] = "-l"; args[argc++] = user; } args[argc++] = machine;#endif args[argc++] = rsync_path; if (blocking_io < 0) { char *cp; if ((cp = strrchr(cmd, '/')) != NULL) cp++; else cp = cmd; if (strcmp(cp, "rsh") == 0 || strcmp(cp, "remsh") == 0) blocking_io = 1; } server_options(args,&argc); if (argc >= MAX_ARGS - 2) goto arg_overflow; } args[argc++] = "."; if (!daemon_over_rsh) { while (remote_argc > 0) { if (argc >= MAX_ARGS - 1) { arg_overflow: rprintf(FERROR, "internal: args[] overflowed in do_cmd()\n"); exit_cleanup(RERR_SYNTAX); } args[argc++] = *remote_argv++; remote_argc--; } } args[argc] = NULL; if (verbose > 3) { for (i = 0; i < argc; i++) rprintf(FCLIENT, "cmd[%d]=%s ", i, args[i]); rprintf(FCLIENT, "\n"); } if (read_batch) { int from_gen_pipe[2]; set_allow_inc_recurse(); if (fd_pair(from_gen_pipe) < 0) { rsyserr(FERROR, errno, "pipe"); exit_cleanup(RERR_IPC); } batch_gen_fd = from_gen_pipe[0]; *f_out_p = from_gen_pipe[1]; *f_in_p = batch_fd; pid = (pid_t)-1; /* no child pid */#ifdef ICONV_CONST setup_iconv();#endif } 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 && !write_batch) whole_file = 1; set_allow_inc_recurse(); pid = local_child(argc, args, f_in_p, f_out_p, child_main);#ifdef ICONV_CONST setup_iconv();#endif } else { pid = piped_child(args, f_in_p, f_out_p);#ifdef ICONV_CONST setup_iconv();#endif if (protect_args && !daemon_over_rsh) send_protected_args(*f_out_p, args); } return pid; oom: out_of_memory("do_cmd"); return 0; /* not reached */}/* The receiving side operates in one of two modes: * * 1. it receives any number of files into a destination directory, * placing them according to their names in the file-list. * * 2. it receives a single file and saves it using the name in the * destination path instead of its file-list name. This requires a * "local name" for writing out the destination file. * * So, our task is to figure out what mode/local-name we need. * For mode 1, we change into the destination directory and return NULL. * For mode 2, we change into the directory containing the destination * file (if we aren't already there) and return the local-name. */static char *get_local_name(struct file_list *flist, char *dest_path){ STRUCT_STAT st; int statret; char *cp; if (verbose > 2) { rprintf(FINFO, "get_local_name count=%d %s\n", file_total, NS(dest_path));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -