From e5bbb969a15c569cf7d37634234a71783f628390 Mon Sep 17 00:00:00 2001 From: James O'Doherty Date: Fri, 22 May 2026 11:37:57 -0400 Subject: Fix PID lifecycle race and improve CLI routing for diagnostic commands --- internal/cli/cli.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 8 deletions(-) (limited to 'internal/cli/cli.go') diff --git a/internal/cli/cli.go b/internal/cli/cli.go index aa4268a..13a4a6b 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -21,7 +21,7 @@ func NewApp(args []string) *App { return &App{Args: args} } -func (a *App) Run() error { +func (a *App) Route() error { // 1. Validate arguments for null bytes to prevent exec failures in the C launcher for i, arg := range a.Args { for j := 0; j < len(arg); j++ { @@ -31,10 +31,34 @@ func (a *App) Run() error { } } - // Handle the internal diagnostic commands first + // Handle the internal diagnostic commands that should run on the HOST + if len(a.Args) > 1 { + switch a.Args[1] { + case "show-config": + return a.showConfig() + } + } + + // Handle subcommands first (profile list, import, configure, delete, stop) + if len(a.Args) > 1 && a.Args[1] == "profile" { + return a.handleProfileCmd() + } + + // If we reach here, we are either wrapping a process or running a command + // that requires isolation (e.g., test-ns, test-args). + return a.Run() +} + +func (a *App) Run() error { + // Handle the internal diagnostic commands that require ISOLATION if len(a.Args) > 1 { switch a.Args[1] { case "test-ns": + if !namespace.IsIsolated() { + if err := namespace.Bootstrap(); err != nil { + return fmt.Errorf("bootstrap failed: %w", err) + } + } ok, msg := namespace.VerifyIsolation() if !ok { return fmt.Errorf("isolation check failed: %s", msg) @@ -42,16 +66,15 @@ func (a *App) Run() error { fmt.Println("Isolation Verified: OK") return nil case "test-args": + if !namespace.IsIsolated() { + if err := namespace.Bootstrap(); err != nil { + return fmt.Errorf("bootstrap failed: %w", err) + } + } return namespace.VerifyArguments(a.Args) } } - // Handle subcommands first (profile list, import, configure, delete, stop) - if len(a.Args) > 1 && a.Args[1] == "profile" { - return a.handleProfileCmd() - } - // ... - cfg := &config.Config{} fs := flag.NewFlagSet("wg-wrap", flag.ExitOnError) @@ -194,3 +217,39 @@ func (a *App) handleProfileCmd() error { return fmt.Errorf("unknown profile subcommand: %s", subCmd) } } + +func (a *App) showConfig() error { + cfg := &config.Config{} + fs := flag.NewFlagSet("wg-wrap", flag.ExitOnError) + fs.StringVar(&cfg.Profile, "profile", "default", "WireGuard profile to use") + fs.StringVar(&cfg.DNSServer, "dns-server", "", "Override DNS server to use") + + // Parse the arguments that follow 'show-config' + if len(a.Args) > 2 { + _ = fs.Parse(a.Args[2:]) + } + + // Determine runtime base directory + runtimeBase := a.RuntimeBaseDir + if runtimeBase == "" { + runtimeBase = os.Getenv("XDG_RUNTIME_DIR") + if runtimeBase == "" { + runtimeBase = fmt.Sprintf("/run/user/%d", os.Getuid()) + } + } + + // Resolve paths + profilePath := namespace.GetProfileNamespacePath(runtimeBase, cfg.Profile) + pidsPath := namespace.GetPidsDirPath(runtimeBase, cfg.Profile) + + fmt.Printf("Configuration:\n") + fmt.Printf(" Profile: %s\n", cfg.Profile) + fmt.Printf(" DNS Server: %s\n", cfg.DNSServer) + fmt.Printf(" Runtime Base: %s\n", runtimeBase) + fmt.Printf(" Profile Path: %s\n", profilePath) + fmt.Printf(" PIDs Path: %s\n", pidsPath) + fmt.Printf(" Isolated: %v\n", namespace.IsIsolated()) + fmt.Printf(" UID: %d\n", os.Getuid()) + + return nil +} -- cgit v1.2.3