Amir Goldstein
2023-Jul-03 06:05 UTC
[PATCH] Add option --log-after to log after moving file into place
This mode is useful when a process is monitoring the log for post-processing of transferred files. With --log-after in local mode, both sender and receiver log to the same log file, so it require --log-file with absolute path. We add %o to the default log format, so it will be easy to tell the logs of the sender from the logs of the receiver: 2023/02/14 14:40:25 [559755] building file list 2023/02/14 14:40:25 [559755] send >f+++++++++ foo 2023/02/14 14:40:25 [559757] recv >f+++++++++ foo 2023/02/14 14:40:25 [559755] sent 111 bytes received 35 bytes 292.00 bytes/sec 2023/02/14 14:40:25 [559755] total size is 4 speedup is 0.03 --- Hi Wayne, This is my first time contributing to rsync. I've sent this also as a pull request a while back: https://github.com/WayneD/rsync/pull/442 I have more pending contributions after this small one goes through, mainly around performance and functionality of syncing files between cifs mounts, which is the main use case of my employer. I am not sure if rsync development follows some release cycle of features vs. bug fixes and did not find any documentation regarding the preferred way of posting features, so trying a patch now is case this is preferred over github PR. Let me know if there is anything else that is required. Thanks, Amir. log.c | 9 +++++++++ main.c | 7 +++++++ options.c | 20 +++++++++++++++++++- receiver.c | 9 ++++++++- rsync.1.md | 1 + rsync.h | 3 ++- 6 files changed, 46 insertions(+), 3 deletions(-) diff --git a/log.c b/log.c index e4ba1cce..a973b519 100644 --- a/log.c +++ b/log.c @@ -47,6 +47,7 @@ extern mode_t orig_umask; extern char *auth_user; extern char *stdout_format; extern char *logfile_format; +extern char *logafter_format; extern char *logfile_name; #ifdef ICONV_CONST extern iconv_t ic_chck; @@ -271,6 +272,8 @@ void rwrite(enum logcode code, const char *buf, int len, int is_utf8) * that the msg gets logged and then sent to stderr after that. */ if (am_daemon > 0 && code != FCLIENT) code = FLOG; + } else if (code == FLOG_AFTER) { + code = FLOG; } else if (send_msgs_to_gen) { assert(!is_utf8); /* Pass the message to our sibling in native charset. */ @@ -813,6 +816,12 @@ void log_item(enum logcode code, struct file_struct *file, int iflags, const cha { const char *s_or_r = am_sender ? "send" : "recv"; + if (code == FLOG_AFTER) { + if (logafter_format && *logafter_format) + log_formatted(FLOG_AFTER, logafter_format, s_or_r, file, NULL, iflags, hlink); + return; + } + if (code != FLOG && stdout_format && !am_server) log_formatted(FCLIENT, stdout_format, s_or_r, file, NULL, iflags, hlink); if (code != FCLIENT && logfile_format && *logfile_format) diff --git a/main.c b/main.c index 0c60b86d..adc14965 100644 --- a/main.c +++ b/main.c @@ -93,7 +93,10 @@ extern int trust_sender_filter; extern int trust_sender_args; extern struct stats stats; extern char *stdout_format; +extern char *logfile_name; extern char *logfile_format; +extern char *logafter_name; +extern int log_after_transfer; extern char *filesfrom_host; extern char *partial_dir; extern char *rsync_path; @@ -1053,6 +1056,10 @@ static int do_recv(int f_in, int f_out, char *local_name) io_start_buffering_in(f_in); io_start_multiplex_out(f_out); + /* Reopen log file for --log-after */ + if (log_after_transfer) + logfile_name = logafter_name; + recv_files(f_in, f_out, local_name); io_flush(FULL_FLUSH); handle_stats(f_in); diff --git a/options.c b/options.c index fd674754..4ab83650 100644 --- a/options.c +++ b/options.c @@ -176,7 +176,9 @@ char *basis_dir[MAX_BASIS_DIRS+1]; char *config_file = NULL; char *shell_cmd = NULL; char *logfile_name = NULL; +char *logafter_name = NULL; char *logfile_format = NULL; +char *logafter_format = NULL; char *stdout_format = NULL; char *password_file = NULL; char *early_input_file = NULL; @@ -205,6 +207,7 @@ static const char *empty_argv[1]; int quiet = 0; int output_motd = 1; int log_before_transfer = 0; +int log_after_transfer = 0; int stdout_format_has_i = 0; int stdout_format_has_o_or_i = 0; int logfile_format_has_i = 0; @@ -769,6 +772,7 @@ static struct poptOption long_options[] = { {"no-m", 0, POPT_ARG_VAL, &prune_empty_dirs, 0, 0, 0 }, {"log-file", 0, POPT_ARG_STRING, &logfile_name, 0, 0, 0 }, {"log-file-format", 0, POPT_ARG_STRING, &logfile_format, 0, 0, 0 }, + {"log-after", 0, POPT_ARG_VAL, &log_after_transfer, 1, 0, 0 }, {"out-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 }, {"log-format", 0, POPT_ARG_STRING, &stdout_format, 0, 0, 0 }, /* DEPRECATED */ {"itemize-changes", 'i', POPT_ARG_NONE, 0, 'i', 0, 0 }, @@ -2359,7 +2363,10 @@ int parse_arguments(int *argc_p, const char ***argv_p) if (logfile_name && !am_daemon) { if (!logfile_format) { - logfile_format = "%i %n%L"; + if (log_after_transfer) + logfile_format = "%o %i %n%L"; + else + logfile_format = "%i %n%L"; logfile_format_has_i = logfile_format_has_o_or_i = 1; } else { if (log_format_has(logfile_format, 'i')) @@ -2371,6 +2378,17 @@ int parse_arguments(int *argc_p, const char ***argv_p) } else if (!am_daemon) logfile_format = NULL; + if (log_after_transfer) { + if (!logfile_name || *logfile_name != '/') { + snprintf(err_buf, sizeof err_buf, + "--log-after requires --log-file=<absolute path>\n"); + goto cleanup; + } + log_before_transfer = 0; + logafter_name = logfile_name; + logafter_format = logfile_format; + } + if (daemon_bwlimit && (!bwlimit || bwlimit > daemon_bwlimit)) bwlimit = daemon_bwlimit; if (bwlimit) { diff --git a/receiver.c b/receiver.c index 6b4b369e..c200ef28 100644 --- a/receiver.c +++ b/receiver.c @@ -28,6 +28,7 @@ extern int am_root; extern int am_server; extern int inc_recurse; extern int log_before_transfer; +extern int log_after_transfer; extern int stdout_format_has_i; extern int logfile_format_has_i; extern int want_xattr_optim; @@ -876,7 +877,9 @@ int recv_files(int f_in, int f_out, char *local_name) /* recv file data */ recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, file, inplace || one_inplace); - log_item(log_code, file, iflags, NULL); + /* log the transfer result after file is moved into place */ + if (!log_after_transfer) + log_item(log_code, file, iflags, NULL); if (want_progress_now) instant_progress(fname); @@ -917,6 +920,10 @@ int recv_files(int f_in, int f_out, char *local_name) } else if (!one_inplace) do_unlink(fnametmp); + /* log the transfer result */ + if (log_after_transfer) + log_item(FLOG_AFTER, file, iflags, NULL); + cleanup_disable(); if (read_batch) diff --git a/rsync.1.md b/rsync.1.md index 2ae6f481..1991e406 100644 --- a/rsync.1.md +++ b/rsync.1.md @@ -543,6 +543,7 @@ has its own detailed description later in this manpage. --out-format=FORMAT output updates using the specified FORMAT --log-file=FILE log what we're doing to the specified FILE --log-file-format=FMT log updates using the specified FMT +--log-after log updates after moving file into place --password-file=FILE read daemon-access password from FILE --early-input=FILE use FILE for daemon's early exec input --list-only list the files instead of copying them diff --git a/rsync.h b/rsync.h index d3709fe0..92d22f61 100644 --- a/rsync.h +++ b/rsync.h @@ -253,8 +253,9 @@ enum logcode { FERROR_XFER=1, FINFO=2, /* sent over socket for any protocol */ FERROR=3, FWARNING=4, /* sent over socket for protocols >= 30 */ FERROR_SOCKET=5, FLOG=6, /* only sent via receiver -> generator pipe */ + FCLIENT=7, /* never transmitted (e.g. server converts to FINFO) */ FERROR_UTF8=8, /* only sent via receiver -> generator pipe */ - FCLIENT=7 /* never transmitted (e.g. server converts to FINFO) */ + FLOG_AFTER=9, /* receiver logs directly to log file */ }; /* Messages types that are sent over the message channel. The logcode -- 2.34.1