//go:build linux package namespace import ( "fmt" "os" "path/filepath" "strconv" "git.theodohertyfamily.com/tools/wg-wrap/internal/paths" ) // PinNamespace touches the namespace path to indicate it is pinned/active. func PinNamespace(pm *paths.PathManager, profile string) error { nsPath := GetProfileNamespacePath(pm, profile) profilesDir := filepath.Dir(nsPath) if err := os.MkdirAll(profilesDir, 0755); err != nil { return fmt.Errorf("failed to create profiles directory: %w", err) } // We write a placeholder file to indicate the profile namespace is pinned. if err := os.WriteFile(nsPath, []byte("active"), 0644); err != nil { return fmt.Errorf("failed to create namespace pin file: %w", err) } return nil } // UnpinNamespace removes the pinned namespace file from the filesystem. // This allows the namespace to be destroyed once the last process exits. func UnpinNamespace(pm *paths.PathManager, profile string) error { nsPath := GetProfileNamespacePath(pm, profile) if _, err := os.Stat(nsPath); os.IsNotExist(err) { return nil } pidsDir := GetPidsDirPath(pm, profile) // Unlink the namespace file if err := os.Remove(nsPath); err != nil { return fmt.Errorf("failed to unpin namespace %s: %w", nsPath, err) } // Try to remove pids directory and empty parent directories _ = os.Remove(pidsDir) _ = os.Remove(filepath.Dir(pidsDir)) _ = os.Remove(filepath.Dir(filepath.Dir(pidsDir))) return nil } // FindActiveProfilePid looks for an active PID running under the specified profile. // Returns 0 if no active process is found. func FindActiveProfilePid(pm *paths.PathManager, profile string) (int, error) { if err := PruneStalePids(pm, profile); err != nil { return 0, fmt.Errorf("failed to prune stale pids: %w", err) } pidsDir := GetPidsDirPath(pm, profile) files, err := os.ReadDir(pidsDir) if err != nil { if os.IsNotExist(err) { return 0, nil } return 0, fmt.Errorf("failed to read pids dir: %w", err) } for _, file := range files { pid, err := strconv.Atoi(file.Name()) if err != nil { continue } // Since we already pruned stale pids, the first file we find is an active pid! return pid, nil } return 0, nil }