Skip to content

fix: exit a daemon session with its transfer's code, log log_exit() - #8360

Merged
oferchen merged 2 commits into
masterfrom
fix/daemon-session-exit-status
Nov 11, 2026
Merged

oferchen merged 2 commits into
masterfrom
fix/daemon-session-exit-status

Conversation

@oferchen

Copy link
Copy Markdown
Owner

Summary

A daemon session's process now exits with the code its transfer ended on, and the daemon log carries log_exit()'s lines in place of an invented summary, as rsync 3.5.1's per-connection child does.

  • Exit status (cleanup.c:213-218, :264):
    • A transfer that runs to its end maps io_error and got_xfer_error to its code via core::exit_code::io_error_exit_code, so a partial transfer exits 23.
    • A fatal transfer exits with the peer's MSG_ERROR_EXIT code if one arrived (io.c:1930), otherwise with its own RERR code.
    • The code goes into session_exit_code. The forked TCP child now exits with it; before, it dropped it via .map(|_| ()) and always exited 0. The stdio and inetd sessions already turned that field into their exit status.
  • Daemon log (log.c:937-963, log.c:310-327):
    • A fatal error logs the cause line and the rsync error: ... (code N) trailer from fatal_report_lines, the same lines the client is sent. This replaces the invented transfer failed to HOST (IP): module=M error=E line.
    • The sent ... received ... total size line is written only for a clean exit or a sender (log.c:941).
    • A partial transfer's end-of-run exit logs no trailer (am_server = 2, cleanup.c:121-122).
  • Abortive close on the receiver now keys on a fatal error rather than on a nonzero code, so a partial push (code 23) keeps the graceful close.

Evidence

All cells are measured against the 3.5.1 tarball build. Server exit statuses come from strace (TCP daemon) or an rsh shim (daemon mode via --rsync-path).

cell 3.5.1 server oc before oc after
pull of a missing file exit 23, totals exit 0, totals exit 23, totals
clean pull exit 0, totals exit 0 exit 0, totals
push with an unreadable source subdir (receiver io_error) exit 23, no totals, no trailer exit 0, totals exit 23, no totals, no trailer
push into a 0555 dir exit 11, mkdir cause + trailer exit 0, transfer failed to line exits with the trailer's code, trailer logged (3, see residuals)

tests/daemon_session_exit_status.rs covers four cells in rsh daemon mode and asserts the server process's exit status and the daemon log. It is deterministic: the oc client reaps its rsh child before exiting. The unreadable-dir cell skips with a printed reason when run as root.

Mutants, all killed:

  • A partial transfer exits 0: two cells.
  • A receiver logs totals on code 23: the io_error push cell.
  • Nothing is logged on a fatal error: the fatal push cell.
  • session_exit_code is not set: three cells.
  • A fatal error exits 1: the fatal push cell.

Residuals

  • Push into a 0555 dir: oc reports code 3 where 3.5.1 reports 11 with the mkdir ... failed cause. That is fix: report a failed destination-root mkdir as RERR_FILEIO on every path #8344 (local) and task 611 (daemon); this PR makes the session exit with whatever code is reported.
  • Trailer role tag: it says [generator= on the receiver; 3.5.1 says [Receiver= for the pre-fork mkdir.
  • Sender totals on a fatal exit: oc's sender logs none, because it has no stats on the error path. 3.5.1 logs them even on error (log.c:941).
  • Missing source: the oc daemon sender logs neither the link_stat ... failed line nor the real totals. That is task 280.
  • Forked TCP child's exit status: a test can't observe it. The parent records it only at --debug=EXIT, and upstream discards it. The strace oracle cell above covers it, and the rsh cells exercise the same session_exit_code path.

Test plan

  • python3 tools/ci/check_rustfmt_all.py
  • Workspace clippy with all features, plus no-default-features clippy of daemon
  • cargo nextest run -p daemon --all-features: 2152 passed
  • Targeted -p bin (178), -p core and -p transfer (530) daemon, inetd and stdio tests
  • 3.5.1 oracle: TCP strace exits and rsh daemon-mode statuses and logs

@github-actions github-actions Bot added the bug Something isn't working label Nov 10, 2026
@oferchen
oferchen force-pushed the fix/daemon-session-exit-status branch 6 times, most recently from c46cecc to 25329f3 Compare November 11, 2026 10:31
A daemon session's process now exits with the code its transfer ended
on, as rsync 3.5.1's per-connection child does (cleanup.c:264).
- A transfer that ran to its end maps its io_error and got_xfer_error
  through cleanup.c:213-218, so a partial transfer exits 23.
- A fatal one exits with its own RERR code, or with the peer's
  MSG_ERROR_EXIT code.
- The forked TCP child and the stdio and inetd sessions all carry it.

The daemon log follows log_exit() (log.c:937-963):
- A fatal error logs the cause and the rsync error trailer it reports,
  replacing the invented "transfer failed to HOST" line.
- The totals line is written only for a clean exit or a sender.

Task 638.
@oferchen
oferchen force-pushed the fix/daemon-session-exit-status branch from 25329f3 to 0e8b77d Compare November 11, 2026 10:49
@oferchen
oferchen merged commit b29a940 into master Nov 11, 2026
2 of 6 checks passed
@oferchen
oferchen deleted the fix/daemon-session-exit-status branch November 11, 2026 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant