diff options
| author | James O'Doherty <james@theodohertyfamily.com> | 2026-05-29 21:24:42 -0400 |
|---|---|---|
| committer | James O'Doherty <james@theodohertyfamily.com> | 2026-05-29 21:24:42 -0400 |
| commit | b098e2845b68ce90f34e4e1e927b4914d0b00ef7 (patch) | |
| tree | 2288efa8e09ab93735e6ae2c25d42ee33634eb2a /internal/cli | |
| parent | 0f3806f77164af99466bfc8c0d7d5f85f9ec078f (diff) | |
fix: resolve deadlocks, routing errors, and test timings in test suite
- fix(cli): resolve Flock self-deadlock in ExecuteCommand cleanup by reusing the existing lockFile handle if already held during premature exit.
- fix(wireguard): configure explicit default route destination (0.0.0.0/0 for IPv4 and ::/0 for IPv6) to avoid netlink "either Dst.IP, Src.IP or Gw must be set" error.
- fix(wireguard): initialize the Tunnel return parameter in StartTunnel to prevent a nil pointer dereference.
- test(e2e): fix argument ordering in waitForLifecycle to pass "test-lifecycle" first, and increase sleep duration of dummy processes to 1.0s to ensure reliable process persistence under race detection.
Diffstat (limited to 'internal/cli')
| -rw-r--r-- | internal/cli/cli.go | 24 |
1 files changed, 21 insertions, 3 deletions
diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 7d5a05c..0e3b8ad 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -167,8 +167,13 @@ func (a *App) ExecuteCommand(cfg *config.Config) error { // Acquire execution lock during configuration and startup inside the namespace lockFile, lockErr := namespace.AcquireProfileLock(pm, cfg.Profile) + var lockFileReleased bool if lockErr == nil { - defer namespace.ReleaseProfileLock(lockFile) + defer func() { + if !lockFileReleased { + namespace.ReleaseProfileLock(lockFile) + } + }() } if err := namespace.PruneStalePids(pm, cfg.Profile); err != nil { @@ -179,8 +184,17 @@ func (a *App) ExecuteCommand(cfg *config.Config) error { } defer func() { - // Re-acquire lock for the entire cleanup sequence to ensure atomic unregister and unpin - cleanupLock, cleanupErr := namespace.AcquireProfileLock(pm, cfg.Profile) + var cleanupLock *os.File + var cleanupErr error + + if lockErr == nil && !lockFileReleased { + // We already hold the lock, so we can just reuse lockFile for cleanup! + cleanupLock = lockFile + } else { + // Re-acquire lock for the entire cleanup sequence to ensure atomic unregister and unpin + cleanupLock, cleanupErr = namespace.AcquireProfileLock(pm, cfg.Profile) + } + if cleanupErr == nil { // 1. Unregister the process first. if err := namespace.UnregisterProcess(pm, cfg.Profile); err != nil { @@ -200,6 +214,9 @@ func (a *App) ExecuteCommand(cfg *config.Config) error { fmt.Printf("failed to unpin namespace: %v\n", err) } } + if lockErr == nil && !lockFileReleased { + lockFileReleased = true + } namespace.ReleaseProfileLock(cleanupLock) } else { // Fallback if lock fails to ensure we still unregister @@ -270,6 +287,7 @@ func (a *App) ExecuteCommand(cfg *config.Config) error { } // We can now release the startup lock and execute the command + lockFileReleased = true namespace.ReleaseProfileLock(lockFile) cmd := exec.Command(cfg.Command[0], cfg.Command[1:]...) |
