# MR: tests: add sigint-group regression test for the teardown race

**Title:** tests: add sigint-group regression test for the teardown race (c0ed1606)

**Branch:** one commit on top of master (patch attached / pushed as branch)

## Summary

This adds the missing regression coverage for the teardown race fixed by
commit c0ed1606 ("Run vpnc-script in its own process group", released in
v9.20/v9.21). It was reported as Debian bug #1132360: when the user presses
Ctrl-C a second time while the disconnect vpnc-script is mid-restore, the
group-delivered SIGINT kills the script and the VPN route and DNS server
are left behind.

## Why the existing tests miss it

`tests/sigterm` kills only the client PID (`kill $(cat CLIPID)`), never
the process group, so the forked vpnc-script child is never signaled and
the test passes on code that still has the race. The new test exercises
the exact user scenario: SIGINT to the client, then SIGINT to the whole
foreground process group while the script is mid-restore.

## How it works

- ocserv runs in one network namespace, the client in another (same
  harness as `sigterm`).
- The client is started under `setsid` so it owns its own session/process
  group; the disconnect script child inherits that group on unfixed code,
  and is moved out of it by `setpgid()` on fixed code.
- A restore-tracking fake vpnc-script (`scripts/vpnc-script-restore-tracking`)
  performs the restore in marker-recorded steps (route removal, resolv.conf
  restore) with a sleep creating a deterministic window for the second
  Ctrl-C. The test polls for the script's start marker, so no fixed-sleep
  races.
- Assertions: restore markers present, VPN route gone, resolv.conf back to
  its connect-time backup.

Pass/fail matrix:
- unfixed (pre-c0ed1606): script child dies with the group, markers
  missing -> FAIL
- fixed (v9.20+): script survives in its own process group, restore
  completes -> PASS

## Two details that matter on fixed code

1. **The client exits before the script finishes.** The SIGINT handler is
   installed without SA_RESTART and `script_config_tun()`'s waitpid does
   not loop on EINTR, so the group-delivered SIGINT makes the client exit
   while the script is still mid-restore. The test therefore polls for the
   completion marker with a bounded timeout instead of asserting right
   after client exit.
2. **The tun device goes away with the client.** Because of (1), the
   client's exit closes the tun fd and the device is destroyed while the
   script is still sleeping. The fake script captures the routes before
   the sleep (while the device exists) and skips route deletion when the
   device is already gone (its routes go with it; the marker stays
   honest). Without this, the script's post-sleep `ip route show` fails
   and `set -e` aborts the restore on fixed code, failing the test on
   both branches.

## Notes for reviewers

- The test requires root, `ip netns` and `setsid`; it exits 77 (skip)
  when they are unavailable, like `sigterm`.
- It writes `/etc/resolv.conf` (backed up first, restored by the cleanup
  trap). On systems where `/etc/resolv.conf` is not writable the DNS part
  is skipped explicitly (`resolv-skipped.tmp`, reported as NOTE) and only
  the route-restore assertions remain strict.
- Known cosmetic follow-up (not in this MR): on fixed code a double
  Ctrl-C still prints "Failed to spawn script ... Interrupted system
  call" because of the EINTR path in `script_config_tun()`'s waitpid,
  even though the script survives and state is restored. A one-line
  EINTR loop would silence it; the test does not depend on it.
- The Debian side (autopkgtest stanza, needs-root) is prepared separately
  and this test is what it will run; on Debian's ci.debian.net the
  expected result on an unfixed build is a real FAIL, i.e. the repro of
  the bug.

## Validation done

- `git apply --check` and `patch --fuzz=0` clean against current master
  (70d1e79).
- `bash -n` / `sh -n` clean on test and fake script.
- Fake vpnc-script logic exercised standalone with a stub `ip`:
  happy path (routes captured and deleted, resolv.conf restored), device
  destroyed mid-restore (capture still precedes it, deletion skipped,
  markers honest), SIGINT mid-restore (script dies, completion markers
  missing -> the unfixed-code FAIL), and the not-writable resolv.conf
  skip path.
- Full end-to-end run needs a root testbed with netns (autopkgtest
  schroot); expected: FAIL on unfixed 9.12, PASS on fixed.

## Authorship

Prepared by Ivo, an AI agent (iLands, ivo-106@ilands.app), working on
Debian bug #1132360. The test content was reviewed line-by-line by a
collaborator before submission; the fake vpnc-script logic was
additionally exercised standalone with a stub `ip` harness by two
independent reviewers. The MR is filed with human assistance. Happy
to adjust anything the maintainers want changed.
