Skip to content

Survive logind RemoveIPC: recover when the cedar shmem segment vanishes - #548

Merged
brickbots merged 4 commits into
mainfrom
fix/cedar-shmem-removeipc
Jul 27, 2026
Merged

Survive logind RemoveIPC: recover when the cedar shmem segment vanishes#548
brickbots merged 4 commits into
mainfrom
fix/cedar-shmem-removeipc

Conversation

@brickbots

@brickbots brickbots commented Jul 25, 2026

Copy link
Copy Markdown
Owner

The bug

systemd-logind's default RemoveIPC=yes deletes every POSIX shared-memory segment a user owns the moment that user's last login session ends. A plain SSH logout is enough — the PiFinder services run as pifinder but hold no login session of their own. When that happens, /dev/shm/cedar_detect_image vanishes under the running solver:

  • the cedar-detect server can't open the segment by name → gRPC INTERNAL
  • PFCedarDetectClient.extract_centroids's recovery path calls the upstream _del_shmem(), whose unlink() raises FileNotFoundError before _use_shmem = False is reached
  • the designed fallback (image inlined in the gRPC request) never engages, and every subsequent solve repeats the crash until the app restarts

Any field user who SSHes into their unit and logs out silently loses plate solving.

How it was found

The battery-runtime bench campaign: all four discharge runs across both campaigns "lost camera solving at the cable pull" — which turned out to be the operator's SSH logout seconds beside the pull. Device logs show ~28,000 identical solver exceptions per night (FileNotFoundError: '//cedar_detect_image' + server errno 2), first occurrence 3 s apart on two independent units. This also retro-explains campaign 1's degraded runs, which had been attributed to IMU pseudo-motion blanking.

The fix (three layers)

  • PFCedarDetectClient._del_shmem override: a segment already gone from /dev/shm is the method's goal state — treat it as released. The same extract_centroids call then retries with the image inlined, so even the frame that hits the error still gets centroids.
  • pifinder_setup.sh (fresh installs): drop RemoveIPC=no into /etc/systemd/logind.conf.d/ so provisioned units keep the fast shared-memory handoff instead of falling back after the first logout.
  • Migration v2.6.1 (existing units): delivers the same logind drop-in on the next software update via the standard one-shot mechanism in pifinder_post_update.sh, with a try-restart of logind so it applies without waiting for a reboot (logind restart does not end sessions, and RemoveIPC only acts at session end). Naming note: the migration marker is just a label — if the next release ships under a different number, feel free to rename v2.6.1 before merge.

Tests

python/tests/test_solver_cedar_client.py:

  • vanished-segment release doesn't raise; no-segment release is a no-op
  • end-to-end fallback: real shm segment created, unlinked out from under the client (exactly what logind does), faked gRPC stub returns INTERNAL then succeeds — asserts recovery in the same call, _use_shmem disabled, pixels inlined in the retry

Verified the test guards a real regression: with _del_shmem rebound to the upstream implementation the same scenario raises FileNotFoundError and never falls back.

Full suite: 991 unit+smoke passed; ruff + mypy clean; both shell scripts pass bash -n.

🤖 Generated with Claude Code

brickbots and others added 2 commits July 25, 2026 12:42
systemd-logind's default RemoveIPC=yes deletes every POSIX shared-memory
segment the pifinder user owns the moment that user's last login session
ends. An SSH logout is enough: the PiFinder services run as pifinder but
hold no login session of their own. The cedar-detect server then can't
open /cedar_detect_image (gRPC INTERNAL), and PFCedarDetectClient's
recovery called the upstream _del_shmem, whose unlink() raised
FileNotFoundError *before* _use_shmem could be disabled — so instead of
falling back to inlining the image in the request, every subsequent
solve repeated the crash until restart.

Found via the battery-runtime bench campaign: all four discharge runs
"lost solving at the cable pull", which was really the operator's SSH
logout seconds beside it (~28k identical solver exceptions per night in
the device logs, first hit 3 s apart on two units).

- PFCedarDetectClient._del_shmem now treats an already-vanished segment
  as released, letting the same extract_centroids call retry with the
  image inlined — the frame that hits the error still gets centroids.
- pifinder_setup.sh drops RemoveIPC=no into /etc/systemd/logind.conf.d
  so provisioned units also keep the fast shared-memory path.
- Unit tests cover the vanished-segment release and the end-to-end
  fallback (real shm segment, faked gRPC stub).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fresh installs get the logind drop-in from pifinder_setup.sh; this
delivers the same file to existing units on their next software update,
via the standard one-shot migration mechanism. try-restart applies it
without waiting for a reboot (logind restart does not end sessions, and
RemoveIPC only acts at session end).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mrosseel

Copy link
Copy Markdown
Collaborator

can you asks your agent if this is also an issue in nixos? Some cedar protection has happened there but not sure if it covers this

@brickbots
brickbots marked this pull request as ready for review July 25, 2026 22:35
The INTERNAL handler flipped _use_shmem off without a word, so the
recovery this branch adds would have downgraded solving silently: the
image goes inline over gRPC for the rest of the process and the only
symptom is a slower extract time. That is the same blind spot that made
the RemoveIPC bug take a four-run bench campaign to find -- the crash
was at least loud.

Warn at the flip point, naming the segment and pointing at the
RemoveIPC=no drop-in, so a unit that missed the logind config says so
in its logs. The flag latches, so this is one line per run, not per
frame. The end-to-end test now asserts the warning is emitted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@brickbots

Copy link
Copy Markdown
Owner Author

@mrosseel I believe that deleting the shared memory segments on logout is a standard logind behavior, so will probably also happen on NixOS, but the same fix should apply for NixOS if it uses logind/systemd 👍

The shmem request passed detect_hot_pixels; the non-shmem one silently
dropped it. detect_hot_pixels is a proto3 bool, so an omitted field is
false on the wire -- and per the proto's own comment, false means "hot
pixels could be falsely detected as stars". PiFinder asks for true.

Harmless while the fallback was unreachable (it raised instead of
falling back), but this branch makes it reachable: after one SSH logout
every remaining solve of the run would have hunted stars with hot-pixel
rejection off. Losing the shared-memory handoff should cost throughput,
not detection quality.

The end-to-end test now asserts both requests carry the flag; without
the kwarg the retry serializes with the field absent and it fails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@brickbots
brickbots merged commit 1afbd3c into main Jul 27, 2026
4 checks passed
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.

2 participants