Skip to content

ADFA-4539: Stop must kill the rsync daemon's per-connection children#131

Merged
luisguzman-adfa merged 1 commit into
mainfrom
feat/ADFA-4539-rsync-stop-children
Jul 3, 2026
Merged

ADFA-4539: Stop must kill the rsync daemon's per-connection children#131
luisguzman-adfa merged 1 commit into
mainfrom
feat/ADFA-4539-rsync-stop-children

Conversation

@luisguzman-adfa

@luisguzman-adfa luisguzman-adfa commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

The bug

The Share daemon runs librsync.so --daemon --no-detach. rsync forks a child per connection to serve it. stop() only did rsyncProcess.destroy(), which SIGTERMs the parent daemon; the forked connection child kept streaming the transfer and reparented to init when the parent died. So hitting Stop mid-share (reported case: you accidentally shared the 80 GB module instead of the 15 GB one) did not stop the transfer.

The fix

After destroying the parent, stop() sweeps /proc and SIGKILLs every rsync process that is ours, matched by our app-private librsync.so path in the process cmdline:

  • The path is under our nativeLibraryDir, unique to this app → matches the daemon parent and every connection child, nothing else. Reparent-proof (cmdline doesn't change on reparent), unlike a ppid/pgid walk.
  • Same-UID kills only (android.os.Process.killProcess = SIGKILL); never our own pid.
  • Covers both the share (daemon) and receive (client) sides.

Ordering: destroy the parent first (stops accepting/forking), then sweep survivors.

Why /proc-cmdline, not a pid tree

minSdk 24; Process.pid()/waitFor(timeout) are API 26+ (the codebase notes this in PRootEngine.killProcess()). Matching our unique binary path in /proc/<pid>/cmdline is portable from 24 and robust.

Changes

  • New pure sync/domain/RsyncProcessMatcher.isOurRsyncProcess(cmdline, binPath) — the testable decision. Unit-tested (5 cases).
  • RsyncManager — remembers rsyncBinPath; stop()killOurRsyncProcesses() scans /proc, reads each cmdline (readCmdline, hand-rolled since Files.readAllBytes is API 26+) and SIGKILLs matches.

Lint / CI

  • MissingTranslation: no impact (no new strings).
  • Verified in sandbox: brace balance, matcher logic 5/5, imports used, android.os.Process fully-qualified. No javac in sandbox — please build :app:testDebugUnitTest + :app:lintDebug.

Test (device)

  1. Share the large module; on a second device start receiving so a connection child is forked and actively transferring.
  2. Host → Stop. Expected: transfer halts; receiver's pull errors/ends. logcat: ADFA-4539: killed lingering rsync pid <n>.
  3. adb shell ps -A | grep rsync on the host after Stop → no lingering rsync.
  4. Normal start/stop with no active connection still works.
  5. Receive side: cancel mid-transfer → client rsync fully gone.

The Share daemon runs 'librsync.so --daemon --no-detach' and forks a child per
connection. stop() only called rsyncProcess.destroy(), which SIGTERMs the parent;
the connection child kept streaming the transfer and reparented to init. So a
Stop mid-share (e.g. after accidentally sharing the 80 GB module instead of the
15 GB one) did not actually stop the transfer.

stop() now, after destroying the parent, sweeps /proc and SIGKILLs every rsync
process that is ours -- identified by our app-private librsync.so path in the
cmdline (unique to this app, so it matches the daemon parent + all connection
children and nothing else, reparent-proof). Same-UID kills only, never our own
pid. Covers both the share (daemon) and receive (client) sides.

- New pure sync/domain/RsyncProcessMatcher.isOurRsyncProcess(cmdline, binPath),
  unit-tested.
- RsyncManager remembers the binary path and adds killOurRsyncProcesses()/
  readCmdline() (minSdk 24: no Process.pid()/waitFor(timeout), hence the /proc
  cmdline match rather than a pid tree).
@luisguzman-adfa luisguzman-adfa merged commit 0345a7e into main Jul 3, 2026
1 check passed
@luisguzman-adfa luisguzman-adfa deleted the feat/ADFA-4539-rsync-stop-children branch July 3, 2026 07:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant