** Description changed:
[ Impact ]
- * reprepro keeps all repository state in Berkeley DB files under db/
- (checksums.db, packages.db, references.db, packagenames.db). It opens them
- without a transactional environment, so they are only consistent if
- reprepro closes them cleanly before it exits.
+ * reprepro keeps all repository state in Berkeley DB files under db/
+ (checksums.db, packages.db, references.db, packagenames.db). It opens them
+ without a transactional environment, so they are only consistent if
+ reprepro closes them cleanly before it exits.
- * reprepro handles SIGTERM, SIGINT, SIGQUIT and SIGABRT by stopping through
- its graceful interruption path, which closes the databases and removes
- db/lockfile. It does not handle SIGHUP. When the terminal reprepro runs on
- goes away, the kernel sends it SIGHUP and it dies at once with its
- databases open. That happens when an ssh session drops, when the process
- that started reprepro on a pty exits, or a reboot.
+ * reprepro handles SIGTERM, SIGINT, SIGQUIT and SIGABRT by stopping through
+ its graceful interruption path, which closes the databases and removes
+ db/lockfile. It does not handle SIGHUP. When the terminal reprepro runs on
+ goes away, the kernel sends it SIGHUP and it dies at once with its
+ databases open. That happens when an ssh session drops, when the process
+ that started reprepro on a pty exits, or a reboot.
- * If the hangup arrives while reprepro is writing (from "Installing (and
- possibly deleting) packages..." until it closes its databases), the
- databases are left corrupt and db/lockfile is left behind.
+ * If the hangup arrives while reprepro is writing (from "Installing (and
+ possibly deleting) packages..." until it closes its databases), the
+ databases are left corrupt and db/lockfile is left behind.
- * Landscape Server mirrors Ubuntu archives with reprepro. Its
- job-handler service runs "reprepro update" on a pty (Twisted
- spawnProcess with usePTY=True) so it can read the line-buffered
- "--show-percent" progress output. Stopping job-handler (e.g. a Landscape
- restart or a reboot) closes the pty, and reprepro
- receives SIGHUP. If a mirror sync is in its install phase at that moment,
- the databases of that distribution are corrupted. Every series and pocket
- of a distribution shares one db/, so the damage is not limited to the
- pocket that was syncing. Landscape takes sync progress only from
- reprepro's "Got N%" lines, so a later sync where reprepro loops on the
- damaged btree shows 0% indefinitely, with no error.
+ * Landscape Server mirrors Ubuntu archives with reprepro. Its
+ job-handler service runs "reprepro update" on a pty (Twisted
+ spawnProcess with usePTY=True) so it can read the line-buffered
+ "--show-percent" progress output. Stopping job-handler (e.g. a Landscape
+ restart or a reboot) closes the pty, and reprepro
+ receives SIGHUP. If a mirror sync is in its install phase at that moment,
+ the databases of that distribution are corrupted. Every series and pocket
+ of a distribution shares one db/, so the damage is not limited to the
+ pocket that was syncing. Landscape takes sync progress only from
+ reprepro's "Got N%" lines, so a later sync where reprepro loops on the
+ damaged btree shows 0% indefinitely, with no error.
[ Test Plan ]
- todo
+ Use a disposable machine or container for the Ubuntu release being tested.
+ Run the commands in Bash, keeping the same shell open throughout. No existing
+ repository or signing key is needed. Do not use production repository data.
+
+ 1. Install the release package and record its version and origin:
+
+ sudo apt update
+ sudo apt install reprepro
+ dpkg-query -W reprepro
+ apt-cache policy reprepro
+
+ 2. Create 2,000 tiny test packages. This gives us enough time to interrupt an
+ import. Keep these files for both runs.
+
+ WORK="$(mktemp -d)"
+ mkdir -p "$WORK/build/DEBIAN" "$WORK/pkgs"
+ for i in $(seq -w 1 2000); do
+ cat > "$WORK/build/DEBIAN/control" <<EOF
+ Package: sru-test-$i
+ Version: 1.0
+ Architecture: all
+ Section: misc
+ Priority: optional
+ Maintainer: Test <[email protected]>
+ Description: test package for LP 2169601
+ EOF
+ dpkg-deb --build "$WORK/build" "$WORK/pkgs/$i.deb" >/dev/null
+ done
+
+ 3. Create an empty repository. Repeat this step whenever the plan asks for a
+ fresh repository; it leaves earlier results untouched.
+
+ REPO="$(mktemp -d "$WORK/repo.XXXXXX")"
+ mkdir -p "$REPO/conf"
+ cat > "$REPO/conf/distributions" <<EOF
+ Codename: test
+ Components: main
+ Architectures: $(dpkg --print-architecture)
+ EOF
+
+ 4. Start an import and send one SIGHUP after the twentieth package reaches
+ the repository. This is the signal reprepro receives on a terminal hangup.
+ The loop waits about 20 seconds for that package, rather than relying on
+ a fixed sleep or just the lock file appearing.
+
+ reprepro -b "$REPO" includedeb test "$WORK"/pkgs/*.deb >"$REPO/import.log"
2>&1 &
+ pid=$!
+ ready="$REPO/pool/main/s/sru-test-0020/sru-test-0020_1.0_all.deb"
+ for attempt in $(seq 1 400); do
+ test -f "$ready" && break
+ kill -0 "$pid" 2>/dev/null || break
+ sleep 0.05
+ done
+ if test -f "$ready" && kill -HUP "$pid"; then
+ status=0
+ wait "$pid" || status=$?
+ echo "exit=$status"
+ else
+ echo "INCONCLUSIVE: missed the import; repeat steps 3-4."
+ fi
+ cat "$REPO/import.log"
+ if test -e "$REPO/db/lockfile"; then echo "lock remains"; else echo "lock
removed"; fi
+
+ Before the fix: expect exit=129 (terminated by SIGHUP) and "lock remains".
+ Bash may also print "Hangup". Database corruption is timing-dependent and
+ is not required to reproduce the bug; the abrupt exit and stale lock are
+ sufficient. Do not delete this lock or reuse this repository.
+
+ If the command has not exited within a minute after the signal, record a
+ failure and retain the log; do not send another SIGHUP to make it pass.
+
+ 5. Rebuild and install reprepro with cherry picked upstream commit
+ e4dfe4f2857a3566fa944527156594024009595a
+
(https://salsa.debian.org/debian/reprepro/-/commit/e4dfe4f2857a3566fa944527156594024009595a)
+
+ Repeat steps 3-4 with the fixed package, using a fresh repository.
+
+ After the fix: expect "Interruption in progress" in the log and
+ "lock removed". A nonzero exit is normal (247 in the tested package), but
+ it must not be the default SIGHUP termination seen before the fix.
+ A warning about unexported indices is also normal: interruption stops the
+ import early; it does not undo packages already imported.
+
+ 6. Check that the interrupted repository is usable, then finish the
+ import:
+
+ timeout 30s reprepro -b "$REPO" check && echo "check passed"
+ timeout 30s reprepro -b "$REPO" list test >"$REPO/list.txt" && echo "list
passed"
+ timeout 60s reprepro -b "$REPO" includedeb test "$WORK"/pkgs/*.deb
>"$REPO/resume.log" 2>&1 && echo "import passed"
+ timeout 30s reprepro -b "$REPO" export test && echo "export passed"
+ timeout 30s reprepro -b "$REPO" check && echo "check passed"
+ timeout 30s reprepro -b "$REPO" list test >"$REPO/list.txt" && wc -l
<"$REPO/list.txt"
+
+ Expected: every "passed" message appears, no command times out, and the
+ final count is 2000. A timeout, error, or missing package is a failure.
+
+ 7. Check the nohup regression: repeat step 3, then repeat step 4 with only its
+ first command changed to the following (keep the rest of step 4 unchanged):
+
+ nohup reprepro -b "$REPO" includedeb test "$WORK"/pkgs/*.deb
+ >"$REPO/import.log" 2>&1 &
+
+ Expected: despite receiving SIGHUP, the import completes with exit=0 and
+ "lock removed". Confirm all packages were imported and the repository is
+ consistent:
+
+ timeout 30s reprepro -b "$REPO" list test >"$REPO/list.txt" && wc -l
<"$REPO/list.txt"
+ timeout 30s reprepro -b "$REPO" check && echo "check passed"
+
+ Expected: 2000 and "check passed". This also works before the fix; it
checks
+ that adding the SIGHUP handler has not broken inherited ignored SIGHUP.
[ Where problems could occur ]
- * The change is in signal setup at the start of main(). If it were wrong,
- the visible effect would be in how reprepro reacts to a terminal hangup.
- Repository contents, the database format and the command line are not
- touched.
+ * The change is in signal setup at the start of main(). If it were wrong,
+ the visible effect would be in how reprepro reacts to a terminal hangup.
+ Repository contents, the database format and the command line are not
+ touched.
- todo
+ * A regression would most likely show up as incorrect process behavior when
+ the controlling terminal hangs up:
+ - reprepro could ignore a SIGHUP that should interrupt it.
+ - reprepro could terminate immediately rather than running cleanup.
+ - nohup-style runs could stop being immune to SIGHUP.
+ - cleanup could fail to remove db/lockfile, blocking later repository work.
+
[ Other Info ]
- todo
+ * Debian merge request, merged into master:
+ https://salsa.debian.org/debian/reprepro/-/merge_requests/22
+
+ * Noble test PPA:
+ https://launchpad.net/~quanchobi/+archive/ubuntu/reprepro-lp2169601
** Changed in: reprepro (Ubuntu)
Assignee: (unassigned) => Jacob Hanks (quanchobi)
** Also affects: reprepro (Debian)
Importance: Undecided
Status: New
** Changed in: reprepro (Debian)
Status: New => Fix Released
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2169601
Title:
SIGHUP during db write corrupts the repository
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/reprepro/+bug/2169601/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs