This patch allows a non-daemon server and/or a client rsync to log what they are doing, similar to how a daemon logs its actions. --- old/cleanup.c +++ new/cleanup.c @@ -21,10 +21,13 @@ #include "rsync.h" +extern int am_server; +extern int am_daemon; extern int io_error; extern int keep_partial; extern int log_got_error; extern char *partial_dir; +extern char *logfile_name; #ifdef HAVE_SIGACTION static struct sigaction sigact; @@ -149,7 +152,7 @@ void _exit_cleanup(int code, const char code = RERR_PARTIAL; } - if (code) + if (code || am_daemon || (am_server && logfile_name)) log_exit(code, file, line); if (verbose > 2) { --- old/clientserver.c +++ new/clientserver.c @@ -44,10 +44,14 @@ extern int protocol_version; extern int io_timeout; extern int no_detach; extern int default_af_hint; +extern int logfile_format_has_i; +extern int logfile_format_has_o_or_i; extern mode_t orig_umask; extern char *bind_address; extern char *sockopts; extern char *config_file; +extern char *logfile_format; +extern char *logfile_name; extern char *files_from; extern char *tmpdir; extern struct chmod_mode_struct *chmod_modes; @@ -55,8 +59,6 @@ extern struct filter_list_struct server_ char *auth_user; int read_only = 0; -int daemon_log_format_has_i = 0; -int daemon_log_format_has_o_or_i = 0; int module_id = -1; struct chmod_mode_struct *daemon_chmod_modes; @@ -329,11 +331,12 @@ static int rsync_module(int f_in, int f_ read_only = 1; if (lp_transfer_logging(i)) { - if (log_format_has(lp_log_format(i), 'i')) - daemon_log_format_has_i = 1; - if (daemon_log_format_has_i - || log_format_has(lp_log_format(i), 'o')) - daemon_log_format_has_o_or_i = 1; + logfile_format = lp_log_format(i); + if (log_format_has(logfile_format, 'i')) + logfile_format_has_i = 1; + if (logfile_format_has_i + || log_format_has(logfile_format, 'o')) + logfile_format_has_o_or_i = 1; } am_root = (MY_UID() == 0); --- old/flist.c +++ new/flist.c @@ -95,15 +95,15 @@ static int show_filelist_p(void) static void start_filelist_progress(char *kind) { - rprintf(FINFO, "%s ... ", kind); + rprintf(FCLIENT, "%s ... ", kind); if (verbose > 1 || do_progress) - rprintf(FINFO, "\n"); + rprintf(FCLIENT, "\n"); rflush(FINFO); } static void emit_filelist_progress(int count) { - rprintf(FINFO, " %d files...\r", count); + rprintf(FCLIENT, " %d files...\r", count); } static void maybe_emit_filelist_progress(int count) @@ -295,7 +295,7 @@ void flist_expand(struct file_list *flis flist->malloced); if (verbose >= 2 && flist->malloced != FLIST_START) { - rprintf(FINFO, "[%s] expand file_list to %.0f bytes, did%s move\n", + rprintf(FCLIENT, "[%s] expand file_list to %.0f bytes, did%s move\n", who_am_i(), (double)sizeof flist->files[0] * flist->malloced, (new_ptr == flist->files) ? " not" : ""); @@ -1077,6 +1077,7 @@ struct file_list *send_file_list(int f, int64 start_write; int use_ff_fd = 0; + rprintf(FLOG, "building file list\n"); if (show_filelist_p()) start_filelist_progress("building file list"); @@ -1343,6 +1344,7 @@ struct file_list *recv_file_list(int f) unsigned short flags; int64 start_read; + rprintf(FLOG, "receiving file list\n"); if (show_filelist_p()) start_filelist_progress("receiving file list"); --- old/generator.c +++ new/generator.c @@ -27,7 +27,7 @@ extern int verbose; extern int dry_run; extern int do_xfers; extern int log_format_has_i; -extern int daemon_log_format_has_i; +extern int logfile_format_has_i; extern int am_root; extern int am_server; extern int am_daemon; @@ -661,7 +661,7 @@ static int try_dests_reg(struct file_str } else if (itemizing) itemize(file, ndx, 0, stp, 0, 0, NULL); if (verbose > 1 && maybe_ATTRS_REPORT) { - code = daemon_log_format_has_i || dry_run + code = logfile_format_has_i || dry_run ? FCLIENT : FINFO; rprintf(code, "%s is uptodate\n", fname); } @@ -684,7 +684,7 @@ static int try_dests_reg(struct file_str if (maybe_ATTRS_REPORT && ((!itemizing && verbose && match_level == 2) || (verbose > 1 && match_level == 3))) { - code = daemon_log_format_has_i || dry_run + code = logfile_format_has_i || dry_run ? FCLIENT : FINFO; rprintf(code, "%s%s\n", fname, match_level == 3 ? " is uptodate" : ""); @@ -758,7 +758,7 @@ static int try_dests_non(struct file_str itemize(file, ndx, 0, &st, changes, 0, lp); } if (verbose > 1 && maybe_ATTRS_REPORT) { - code = daemon_log_format_has_i || dry_run + code = logfile_format_has_i || dry_run ? FCLIENT : FINFO; rprintf(code, "%s is uptodate\n", fname); } @@ -1327,9 +1327,9 @@ void generate_files(int f_out, struct fi if (protocol_version >= 29) { itemizing = 1; maybe_ATTRS_REPORT = log_format_has_i ? 0 : ATTRS_REPORT; - code = daemon_log_format_has_i ? 0 : FLOG; + code = logfile_format_has_i ? 0 : FLOG; } else if (am_daemon) { - itemizing = daemon_log_format_has_i && do_xfers; + itemizing = logfile_format_has_i && do_xfers; maybe_ATTRS_REPORT = ATTRS_REPORT; code = itemizing || !do_xfers ? FCLIENT : FINFO; } else if (!am_server) { --- old/log.c +++ new/log.c @@ -44,18 +44,19 @@ extern int protocol_version; extern int preserve_times; extern int log_format_has_i; extern int log_format_has_o_or_i; -extern int daemon_log_format_has_o_or_i; +extern int logfile_format_has_o_or_i; extern mode_t orig_umask; extern char *auth_user; extern char *log_format; +extern char *logfile_format; +extern char *logfile_name; #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H extern iconv_t ic_chck; #endif static int log_initialised; static int logfile_was_closed; -static char *logfname; -static FILE *logfile; +static FILE *logfile_fp; struct stats stats; int log_got_error = 0; @@ -109,10 +110,10 @@ static void logit(int priority, char *bu { if (logfile_was_closed) logfile_reopen(); - if (logfile) { - fprintf(logfile,"%s [%d] %s", + if (logfile_fp) { + fprintf(logfile_fp, "%s [%d] %s", timestring(time(NULL)), (int)getpid(), buf); - fflush(logfile); + fflush(logfile_fp); } else { syslog(priority, "%s", buf); } @@ -145,14 +146,14 @@ static void syslog_init() static void logfile_open(void) { mode_t old_umask = umask(022 | orig_umask); - logfile = fopen(logfname, "a"); + logfile_fp = fopen(logfile_name, "a"); umask(old_umask); - if (!logfile) { + if (!logfile_fp) { int fopen_errno = errno; /* Rsync falls back to using syslog on failure. */ syslog_init(); rsyserr(FERROR, fopen_errno, - "failed to open log-file %s", logfname); + "failed to open log-file %s", logfile_name); rprintf(FINFO, "Ignoring \"log file\" setting.\n"); } } @@ -171,9 +172,11 @@ void log_init(void) t = time(NULL); localtime(&t); - /* optionally use a log file instead of syslog */ - logfname = lp_log_file(); - if (logfname && *logfname) + /* Optionally use a log file instead of syslog. (Non-daemon + * rsyncs will have already set logfile_name, as needed.) */ + if (am_daemon) + logfile_name = lp_log_file(); + if (logfile_name && *logfile_name) logfile_open(); else syslog_init(); @@ -181,10 +184,10 @@ void log_init(void) void logfile_close(void) { - if (logfile) { + if (logfile_fp) { logfile_was_closed = 1; - fclose(logfile); - logfile = NULL; + fclose(logfile_fp); + logfile_fp = NULL; } } @@ -243,9 +246,9 @@ void rwrite(enum logcode code, char *buf if (code == FCLIENT) code = FINFO; - else if (am_daemon) { + else if (am_daemon || logfile_name) { static int in_block; - char msg[2048]; + char msg[2048], *s; int priority = code == FERROR ? LOG_WARNING : LOG_INFO; if (in_block) @@ -254,10 +257,11 @@ void rwrite(enum logcode code, char *buf if (!log_initialised) log_init(); strlcpy(msg, buf, MIN((int)sizeof msg, len + 1)); - logit(priority, msg); + for (s = msg; *s == '\n' && s[1]; s++) {} + logit(priority, s); in_block = 0; - if (code == FLOG || !am_server) + if (code == FLOG || (am_daemon && !am_server)) return; } else if (code == FLOG) return; @@ -403,26 +407,14 @@ void rflush(enum logcode code) { FILE *f = NULL; - if (am_daemon) { + if (am_daemon || code == FLOG) return; - } - if (code == FLOG) { - return; - } - - if (code == FERROR) { + if (code == FERROR || am_server) f = stderr; - } - - if (code == FINFO) { - if (am_server) - f = stderr; - else - f = stdout; - } + else + f = stdout; - if (!f) exit_cleanup(RERR_MESSAGEIO); fflush(f); } @@ -695,12 +687,12 @@ void log_item(struct file_struct *file, { char *s_or_r = am_sender ? "send" : "recv"; - if (lp_transfer_logging(module_id)) { - log_formatted(FLOG, lp_log_format(module_id), s_or_r, - file, initial_stats, iflags, hlink); - } else if (log_format && !am_server) { + if (log_format && !am_server) { log_formatted(FNAME, log_format, s_or_r, file, initial_stats, iflags, hlink); + } else if (logfile_format) { + log_formatted(FLOG, logfile_format, s_or_r, + file, initial_stats, iflags, hlink); } } @@ -712,7 +704,7 @@ void maybe_log_item(struct file_struct * || log_format_has_i > 1 || (verbose > 1 && log_format_has_i)); int local_change = iflags & ITEM_LOCAL_CHANGE && significant_flags; if (am_server) { - if (am_daemon && !dry_run && see_item) + if (logfile_name && !dry_run && see_item) log_item(file, &stats, iflags, buf); } else if (see_item || local_change || *buf || (S_ISDIR(file->mode) && significant_flags)) @@ -740,10 +732,10 @@ void log_delete(char *fname, int mode) ITEM_DELETED, NULL); } - if (!am_daemon || dry_run || !lp_transfer_logging(module_id)) + if (!logfile_name || dry_run || !logfile_format) return; - fmt = daemon_log_format_has_o_or_i ? lp_log_format(module_id) : "deleting %n"; + fmt = logfile_format_has_o_or_i ? logfile_format : "deleting %n"; log_formatted(FLOG, fmt, "del.", &file, &stats, ITEM_DELETED, NULL); } --- old/main.c +++ new/main.c @@ -168,7 +168,6 @@ static void handle_stats(int f) return; if (am_daemon) { - log_exit(0, __FILE__, __LINE__); if (f == -1 || !am_sender) return; } --- old/options.c +++ new/options.c @@ -146,6 +146,8 @@ char *basis_dir[MAX_BASIS_DIRS+1]; char *config_file = NULL; char *shell_cmd = NULL; char *log_format = NULL; +char *logfile_name = NULL; +char *logfile_format = NULL; char *password_file = NULL; char *rsync_path = RSYNC_PATH; char *backup_dir = NULL; @@ -162,7 +164,9 @@ int verbose = 0; int quiet = 0; int log_before_transfer = 0; int log_format_has_i = 0; +int logfile_format_has_i = 0; int log_format_has_o_or_i = 0; +int logfile_format_has_o_or_i = 0; int always_checksum = 0; int list_only = 0; @@ -359,6 +363,7 @@ void usage(enum logcode F) rprintf(F," --progress show progress during transfer\n"); rprintf(F," -P same as --partial --progress\n"); rprintf(F," -i, --itemize-changes output a change-summary for all updates\n"); + rprintf(F," --log-file=FILE output what we're doing to a log file\n"); rprintf(F," --log-format=FORMAT output filenames using the specified format\n"); rprintf(F," --password-file=FILE read password from FILE\n"); rprintf(F," --list-only list the files instead of copying them\n"); @@ -492,6 +497,7 @@ static struct poptOption long_options[] {"partial-dir", 0, POPT_ARG_STRING, &partial_dir, 0, 0, 0 }, {"delay-updates", 0, POPT_ARG_NONE, &delay_updates, 0, 0, 0 }, {"prune-empty-dirs",'m', POPT_ARG_NONE, &prune_empty_dirs, 0, 0, 0 }, + {"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 }, {"log-format", 0, POPT_ARG_STRING, &log_format, 0, 0, 0 }, {"itemize-changes", 'i', POPT_ARG_NONE, 0, 'i', 0, 0 }, {"bwlimit", 0, POPT_ARG_INT, &bwlimit, 0, 0, 0 }, @@ -1311,6 +1317,21 @@ int parse_arguments(int *argc, const cha if (log_format_has_i || log_format_has(log_format, 'o')) log_format_has_o_or_i = 1; + if (am_daemon) + logfile_name = NULL; + else if (logfile_name) { + if (am_server) { + logfile_format = "%i %n%L"; + logfile_format_has_i = logfile_format_has_o_or_i = 1; + } else if (log_format) { + logfile_format = log_format; + logfile_format_has_i = log_format_has_i; + logfile_format_has_o_or_i = log_format_has_o_or_i; + } + log_before_transfer = !am_server; + log_init(); + } + if (daemon_bwlimit && (!bwlimit || bwlimit > daemon_bwlimit)) bwlimit = daemon_bwlimit; if (bwlimit) { --- old/progress.c +++ new/progress.c @@ -103,7 +103,7 @@ static void rprint_progress(OFF_T ofs, O stats.num_files); } else strcpy(eol, "\r"); - rprintf(FINFO, "%12s %3d%% %7.2f%s %4d:%02d:%02d%s", + rprintf(FCLIENT, "%12s %3d%% %7.2f%s %4d:%02d:%02d%s", human_num(ofs), pct, rate, units, remain_h, remain_m, remain_s, eol); } --- old/receiver.c +++ new/receiver.c @@ -27,7 +27,7 @@ extern int am_server; extern int do_progress; extern int log_before_transfer; extern int log_format_has_i; -extern int daemon_log_format_has_i; +extern int logfile_format_has_i; extern int csum_length; extern int read_batch; extern int write_batch; @@ -341,8 +341,7 @@ int recv_files(int f_in, struct file_lis struct file_struct *file; struct stats initial_stats; int save_make_backups = make_backups; - int itemizing = am_daemon ? daemon_log_format_has_i - : !am_server && log_format_has_i; + int itemizing = am_server ? logfile_format_has_i : log_format_has_i; int max_phase = protocol_version >= 29 ? 2 : 1; int i, recv_ok; --- old/rsync.c +++ new/rsync.c @@ -32,7 +32,7 @@ extern int verbose; extern int dry_run; -extern int daemon_log_format_has_i; +extern int logfile_format_has_i; extern int preserve_perms; extern int preserve_executability; extern int preserve_times; @@ -218,7 +218,7 @@ int set_file_attrs(char *fname, struct f #endif if (verbose > 1 && flags & ATTRS_REPORT) { - enum logcode code = daemon_log_format_has_i || dry_run + enum logcode code = logfile_format_has_i || dry_run ? FCLIENT : FINFO; if (updated) rprintf(code, "%s\n", fname); --- old/rsync.h +++ new/rsync.h @@ -158,8 +158,8 @@ /* Log-message categories. Only FERROR and FINFO get sent over the socket. - * FLOG and FCLIENT are only used on the daemon side for custom logging, - * while FNAME is only used on the client side. */ + * FLOG only goes to the log file, not to the client; FCLIENT is the opposite. + * FNAME is a client-side message when outputting a filename on its own. */ enum logcode { FERROR=1, FINFO=2, FLOG=3, FCLIENT=4, FNAME=5, FSOCKERR=6 }; /* Messages types that are sent over the message channel. The logcode --- old/rsync.yo +++ new/rsync.yo @@ -387,6 +387,7 @@ to the detailed description below for a --progress show progress during transfer -P same as --partial --progress -i, --itemize-changes output a change-summary for all updates + --log-file=FILE output what we're doing to a log file --log-format=FORMAT output filenames using the specified format --password-file=FILE read password from FILE --list-only list the files instead of copying them @@ -1430,6 +1431,23 @@ the string "*deleting" for each item tha you are talking to a recent enough rsync that it logs deletions instead of outputting them as a verbose message). +dit(bf(--log-file=FILE)) This option causes rsync to log what it is doing +to a file. This is similar to the logging that a daemon does, but can be +requested for the client side and/or the server side of a non-daemon +transfer. If specified as a client option, transfer logging will in effect +if the bf(--log-format) option was either specified or implied (e.g. +bf(--verbose) implies a basic log format). If explicitly sent to a server +via the bf(--rsync-path) option, transfer logging will always occur using +the default bf(--itemize-changes) format. + +Here's a example command that requests the remote side to log what is +happening: + +verb( rsync -av --rsync-path="path --log-file=/tmp/rlog" src/ dest/) + +This is very useful if you need to debug why a connection is closing +unexpectedly. + dit(bf(--log-format=FORMAT)) This allows you to specify exactly what the rsync client outputs to the user on a per-file basis. The format is a text string containing embedded single-character escape sequences prefixed with --- old/sender.c +++ new/sender.c @@ -25,7 +25,7 @@ extern int am_server; extern int am_daemon; extern int log_before_transfer; extern int log_format_has_i; -extern int daemon_log_format_has_i; +extern int logfile_format_has_i; extern int csum_length; extern int append_mode; extern int io_error; @@ -215,8 +215,7 @@ void send_files(struct file_list *flist, int phase = 0, max_phase = protocol_version >= 29 ? 2 : 1; struct stats initial_stats; int save_make_backups = make_backups; - int itemizing = am_daemon ? daemon_log_format_has_i - : !am_server && log_format_has_i; + int itemizing = am_server ? logfile_format_has_i : log_format_has_i; int f_xfer = write_batch < 0 ? batch_fd : f_out; int i, j;