From f3e1268b333ecd1f1e7ccce2aad8f1d4f915532c Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Mon, 24 Nov 2025 23:39:30 -0800 Subject: [PATCH] refactor: remove deprecated global daemon support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Global daemon support has been deprecated for most of the project's lifetime. This change removes the dead code entirely: - Remove --global and --migrate-to-global daemon flags - Remove runGlobalDaemon() and migrateToGlobalDaemon() functions - Remove shouldUseGlobalDaemon() and getGlobalBeadsDir() functions - Simplify functions that had global bool parameters - Remove warning about old global socket in getSocketPath() This reduces code complexity and removes 238 net lines of unused code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- cmd/bd/daemon.go | 99 ++++++++++----------------- cmd/bd/daemon_autostart.go | 60 +++-------------- cmd/bd/daemon_config.go | 47 ++----------- cmd/bd/daemon_lifecycle.go | 134 ++++++------------------------------- cmd/bd/daemon_server.go | 34 ---------- 5 files changed, 68 insertions(+), 306 deletions(-) diff --git a/cmd/bd/daemon.go b/cmd/bd/daemon.go index ebbb40ab..0e4b33d7 100644 --- a/cmd/bd/daemon.go +++ b/cmd/bd/daemon.go @@ -45,22 +45,20 @@ Run 'bd daemon' with no flags to see available options.`, status, _ := cmd.Flags().GetBool("status") health, _ := cmd.Flags().GetBool("health") metrics, _ := cmd.Flags().GetBool("metrics") - migrateToGlobal, _ := cmd.Flags().GetBool("migrate-to-global") interval, _ := cmd.Flags().GetDuration("interval") autoCommit, _ := cmd.Flags().GetBool("auto-commit") autoPush, _ := cmd.Flags().GetBool("auto-push") logFile, _ := cmd.Flags().GetString("log") - global, _ := cmd.Flags().GetBool("global") // If no operation flags provided, show help - if !start && !stop && !status && !health && !metrics && !migrateToGlobal { + if !start && !stop && !status && !health && !metrics { _ = cmd.Help() return } // If auto-commit/auto-push flags weren't explicitly provided, read from config - // (skip if --stop, --status, --health, --metrics, or --migrate-to-global) - if start && !stop && !status && !health && !metrics && !migrateToGlobal && !global { + // (skip if --stop, --status, --health, --metrics) + if start && !stop && !status && !health && !metrics { if !cmd.Flags().Changed("auto-commit") { if dbPath := beads.FindDatabasePath(); dbPath != "" { ctx := context.Background() @@ -92,29 +90,24 @@ Run 'bd daemon' with no flags to see available options.`, os.Exit(1) } - pidFile, err := getPIDFilePath(global) + pidFile, err := getPIDFilePath() if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } if status { - showDaemonStatus(pidFile, global) + showDaemonStatus(pidFile) return } if health { - showDaemonHealth(global) + showDaemonHealth() return } if metrics { - showDaemonMetrics(global) - return - } - - if migrateToGlobal { - migrateToGlobalDaemon() + showDaemonMetrics() return } @@ -137,7 +130,7 @@ Run 'bd daemon' with no flags to see available options.`, // Check if daemon is already running if isRunning, pid := isDaemonRunning(pidFile); isRunning { // Check if running daemon has compatible version - socketPath := getSocketPathForPID(pidFile, global) + socketPath := getSocketPathForPID(pidFile) if client, err := rpc.TryConnectWithTimeout(socketPath, 1*time.Second); err == nil && client != nil { health, healthErr := client.Health() _ = client.Close() @@ -145,7 +138,7 @@ Run 'bd daemon' with no flags to see available options.`, // If we can check version and it's compatible, exit if healthErr == nil && health.Compatible { fmt.Fprintf(os.Stderr, "Error: daemon already running (PID %d, version %s)\n", pid, health.Version) - fmt.Fprintf(os.Stderr, "Use 'bd daemon --stop%s' to stop it first\n", boolToFlag(global, " --global")) + fmt.Fprintf(os.Stderr, "Use 'bd daemon --stop' to stop it first\n") os.Exit(1) } @@ -159,22 +152,14 @@ Run 'bd daemon' with no flags to see available options.`, } else { // Can't check version - assume incompatible fmt.Fprintf(os.Stderr, "Error: daemon already running (PID %d)\n", pid) - fmt.Fprintf(os.Stderr, "Use 'bd daemon --stop%s' to stop it first\n", boolToFlag(global, " --global")) + fmt.Fprintf(os.Stderr, "Use 'bd daemon --stop' to stop it first\n") os.Exit(1) } } } - // Global daemon doesn't support auto-commit/auto-push (no sync loop) - if global && (autoCommit || autoPush) { - fmt.Fprintf(os.Stderr, "Error: --auto-commit and --auto-push are not supported with --global\n") - fmt.Fprintf(os.Stderr, "Hint: global daemon runs in routing mode and doesn't perform background sync\n") - fmt.Fprintf(os.Stderr, " Use local daemon (without --global) for auto-commit/auto-push features\n") - os.Exit(1) - } - - // Validate we're in a git repo (skip for global daemon) - if !global && !isGitRepo() { + // Validate we're in a git repo + if !isGitRepo() { fmt.Fprintf(os.Stderr, "Error: not in a git repository\n") fmt.Fprintf(os.Stderr, "Hint: run 'git init' to initialize a repository\n") os.Exit(1) @@ -188,30 +173,24 @@ Run 'bd daemon' with no flags to see available options.`, } // Warn if starting daemon in a git worktree - if !global { - // Ensure dbPath is set for warning - if dbPath == "" { - if foundDB := beads.FindDatabasePath(); foundDB != "" { - dbPath = foundDB - } - } - if dbPath != "" { - warnWorktreeDaemon(dbPath) + // Ensure dbPath is set for warning + if dbPath == "" { + if foundDB := beads.FindDatabasePath(); foundDB != "" { + dbPath = foundDB } } + if dbPath != "" { + warnWorktreeDaemon(dbPath) + } // Start daemon - scope := "local" - if global { - scope = "global" - } - fmt.Printf("Starting bd daemon (%s, interval: %v, auto-commit: %v, auto-push: %v)\n", - scope, interval, autoCommit, autoPush) + fmt.Printf("Starting bd daemon (interval: %v, auto-commit: %v, auto-push: %v)\n", + interval, autoCommit, autoPush) if logFile != "" { fmt.Printf("Logging to: %s\n", logFile) } - startDaemon(interval, autoCommit, autoPush, logFile, pidFile, global) + startDaemon(interval, autoCommit, autoPush, logFile, pidFile) }, } @@ -224,9 +203,7 @@ func init() { daemonCmd.Flags().Bool("status", false, "Show daemon status") daemonCmd.Flags().Bool("health", false, "Check daemon health and metrics") daemonCmd.Flags().Bool("metrics", false, "Show detailed daemon metrics") - daemonCmd.Flags().Bool("migrate-to-global", false, "Migrate from local to global daemon") daemonCmd.Flags().String("log", "", "Log file path (default: .beads/daemon.log)") - daemonCmd.Flags().Bool("global", false, "Run as global daemon (socket at ~/.beads/bd.sock)") daemonCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output JSON format") rootCmd.AddCommand(daemonCmd) } @@ -243,7 +220,7 @@ func computeDaemonParentPID() int { } return os.Getppid() } -func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, pidFile string, global bool) { +func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, pidFile string) { logF, log := setupDaemonLogger(logPath) defer func() { _ = logF.Close() }() @@ -255,16 +232,16 @@ func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, p defer func() { if r := recover(); r != nil { log.log("PANIC: daemon crashed: %v", r) - + // Capture stack trace stackBuf := make([]byte, 4096) stackSize := runtime.Stack(stackBuf, false) stackTrace := string(stackBuf[:stackSize]) log.log("Stack trace:\n%s", stackTrace) - + // Write crash report to daemon-error file for user visibility var beadsDir string - if !global && dbPath != "" { + if dbPath != "" { beadsDir = filepath.Dir(dbPath) } else if foundDB := beads.FindDatabasePath(); foundDB != "" { beadsDir = filepath.Dir(foundDB) @@ -288,17 +265,14 @@ func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, p }() // Determine database path first (needed for lock file metadata) - daemonDBPath := "" - if !global { - daemonDBPath = dbPath - if daemonDBPath == "" { - if foundDB := beads.FindDatabasePath(); foundDB != "" { - daemonDBPath = foundDB - } else { - log.log("Error: no beads database found") - log.log("Hint: run 'bd init' to create a database or set BEADS_DB environment variable") - return // Use return instead of os.Exit to allow defers to run - } + daemonDBPath := dbPath + if daemonDBPath == "" { + if foundDB := beads.FindDatabasePath(); foundDB != "" { + daemonDBPath = foundDB + } else { + log.log("Error: no beads database found") + log.log("Hint: run 'bd init' to create a database or set BEADS_DB environment variable") + return // Use return instead of os.Exit to allow defers to run } } @@ -311,11 +285,6 @@ func runDaemonLoop(interval time.Duration, autoCommit, autoPush bool, logPath, p log.log("Daemon started (interval: %v, auto-commit: %v, auto-push: %v)", interval, autoCommit, autoPush) - if global { - runGlobalDaemon(ctx, log) - return - } - // Check for multiple .db files (ambiguity error) beadsDir := filepath.Dir(daemonDBPath) matches, err := filepath.Glob(filepath.Join(beadsDir, "*.db")) diff --git a/cmd/bd/daemon_autostart.go b/cmd/bd/daemon_autostart.go index 4a29b45d..0f90a1fc 100644 --- a/cmd/bd/daemon_autostart.go +++ b/cmd/bd/daemon_autostart.go @@ -33,22 +33,11 @@ func shouldAutoStartDaemon() bool { return config.GetBool("auto-start-daemon") // Defaults to true } -// shouldUseGlobalDaemon determines if global daemon should be preferred -// based on heuristics (multi-repo detection) -// Note: Global daemon is deprecated; this always returns false for now -func shouldUseGlobalDaemon() bool { - // Global daemon support is deprecated - // Always use local daemon (per-project .beads/ socket) - // Previously supported BEADS_PREFER_GLOBAL_DAEMON env var, but global - // daemon has issues with multi-workspace git workflows - return false -} // restartDaemonForVersionMismatch stops the old daemon and starts a new one // Returns true if restart was successful func restartDaemonForVersionMismatch() bool { - // Use local daemon (global is deprecated) - pidFile, err := getPIDFilePath(false) + pidFile, err := getPIDFilePath() if err != nil { debug.Logf("failed to get PID file path: %v", err) return false @@ -173,8 +162,8 @@ func tryAutoStartDaemon(socketPath string) bool { return true } - socketPath, isGlobal := determineSocketMode(socketPath) - return startDaemonProcess(socketPath, isGlobal) + socketPath = determineSocketPath(socketPath) + return startDaemonProcess(socketPath) } func debugLog(msg string, args ...interface{}) { @@ -242,40 +231,22 @@ func handleExistingSocket(socketPath string) bool { return false } -func determineSocketMode(socketPath string) (string, bool) { - home, err := os.UserHomeDir() - if err != nil { - return socketPath, false - } - - globalSocket := filepath.Join(home, ".beads", "bd.sock") - if socketPath == globalSocket { - return socketPath, true - } - - if shouldUseGlobalDaemon() { - debugLog("detected multiple repos, auto-starting global daemon") - return globalSocket, true - } - - return socketPath, false +func determineSocketPath(socketPath string) string { + return socketPath } -func startDaemonProcess(socketPath string, isGlobal bool) bool { +func startDaemonProcess(socketPath string) bool { binPath, err := os.Executable() if err != nil { binPath = os.Args[0] } args := []string{"daemon", "--start"} - if isGlobal { - args = append(args, "--global") - } cmd := exec.Command(binPath, args...) setupDaemonIO(cmd) - if !isGlobal && dbPath != "" { + if dbPath != "" { cmd.Dir = filepath.Dir(dbPath) } @@ -389,22 +360,9 @@ func recordDaemonStartFailure() { } // getSocketPath returns the daemon socket path based on the database location -// Always returns local socket path (.beads/bd.sock relative to database) +// Returns local socket path (.beads/bd.sock relative to database) func getSocketPath() string { - // Always use local socket (same directory as database: .beads/bd.sock) - localSocket := filepath.Join(filepath.Dir(dbPath), "bd.sock") - - // Warn if old global socket exists - if home, err := os.UserHomeDir(); err == nil { - globalSocket := filepath.Join(home, ".beads", "bd.sock") - if _, err := os.Stat(globalSocket); err == nil { - fmt.Fprintf(os.Stderr, "Warning: Found old global daemon socket at %s\n", globalSocket) - fmt.Fprintf(os.Stderr, "Global sockets are deprecated. Each project now uses its own local daemon.\n") - fmt.Fprintf(os.Stderr, "To migrate: Stop the global daemon and restart with 'bd daemon' in each project.\n") - } - } - - return localSocket + return filepath.Join(filepath.Dir(dbPath), "bd.sock") } // emitVerboseWarning prints a one-line warning when falling back to direct mode diff --git a/cmd/bd/daemon_config.go b/cmd/bd/daemon_config.go index 714758b9..a052a50b 100644 --- a/cmd/bd/daemon_config.go +++ b/cmd/bd/daemon_config.go @@ -9,21 +9,6 @@ import ( "github.com/steveyegge/beads/internal/beads" ) -// getGlobalBeadsDir returns the global beads directory (~/.beads) -func getGlobalBeadsDir() (string, error) { - home, err := os.UserHomeDir() - if err != nil { - return "", fmt.Errorf("cannot get home directory: %w", err) - } - - beadsDir := filepath.Join(home, ".beads") - if err := os.MkdirAll(beadsDir, 0700); err != nil { - return "", fmt.Errorf("cannot create global beads directory: %w", err) - } - - return beadsDir, nil -} - // ensureBeadsDir ensures the local beads directory exists (.beads in the current workspace) func ensureBeadsDir() (string, error) { var beadsDir string @@ -74,26 +59,14 @@ func getEnvBool(key string, defaultValue bool) bool { } // getSocketPathForPID determines the socket path for a given PID file -func getSocketPathForPID(pidFile string, global bool) string { - if global { - home, _ := os.UserHomeDir() - return filepath.Join(home, ".beads", "bd.sock") - } - // Local daemon: socket is in same directory as PID file +func getSocketPathForPID(pidFile string) string { + // Socket is in same directory as PID file return filepath.Join(filepath.Dir(pidFile), "bd.sock") } // getPIDFilePath returns the path to the daemon PID file -func getPIDFilePath(global bool) (string, error) { - var beadsDir string - var err error - - if global { - beadsDir, err = getGlobalBeadsDir() - } else { - beadsDir, err = ensureBeadsDir() - } - +func getPIDFilePath() (string, error) { + beadsDir, err := ensureBeadsDir() if err != nil { return "", err } @@ -101,20 +74,12 @@ func getPIDFilePath(global bool) (string, error) { } // getLogFilePath returns the path to the daemon log file -func getLogFilePath(userPath string, global bool) (string, error) { +func getLogFilePath(userPath string) (string, error) { if userPath != "" { return userPath, nil } - var beadsDir string - var err error - - if global { - beadsDir, err = getGlobalBeadsDir() - } else { - beadsDir, err = ensureBeadsDir() - } - + beadsDir, err := ensureBeadsDir() if err != nil { return "", err } diff --git a/cmd/bd/daemon_lifecycle.go b/cmd/bd/daemon_lifecycle.go index b9dc16f8..a07fb5ca 100644 --- a/cmd/bd/daemon_lifecycle.go +++ b/cmd/bd/daemon_lifecycle.go @@ -40,20 +40,15 @@ func formatUptime(seconds float64) string { } // showDaemonStatus displays the current daemon status -func showDaemonStatus(pidFile string, global bool) { +func showDaemonStatus(pidFile string) { if isRunning, pid := isDaemonRunning(pidFile); isRunning { - scope := "local" - if global { - scope = "global" - } - var started string if info, err := os.Stat(pidFile); err == nil { started = info.ModTime().Format("2006-01-02 15:04:05") } var logPath string - if lp, err := getLogFilePath("", global); err == nil { + if lp, err := getLogFilePath(""); err == nil { if _, err := os.Stat(lp); err == nil { logPath = lp } @@ -63,7 +58,6 @@ func showDaemonStatus(pidFile string, global bool) { status := map[string]interface{}{ "running": true, "pid": pid, - "scope": scope, } if started != "" { status["started"] = started @@ -75,7 +69,7 @@ func showDaemonStatus(pidFile string, global bool) { return } - fmt.Printf("Daemon is running (PID %d, %s)\n", pid, scope) + fmt.Printf("Daemon is running (PID %d)\n", pid) if started != "" { fmt.Printf(" Started: %s\n", started) } @@ -92,23 +86,13 @@ func showDaemonStatus(pidFile string, global bool) { } // showDaemonHealth displays daemon health information -func showDaemonHealth(global bool) { - var socketPath string - if global { - home, err := os.UserHomeDir() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: cannot get home directory: %v\n", err) - os.Exit(1) - } - socketPath = filepath.Join(home, ".beads", "bd.sock") - } else { - beadsDir, err := ensureBeadsDir() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - socketPath = filepath.Join(beadsDir, "bd.sock") +func showDaemonHealth() { + beadsDir, err := ensureBeadsDir() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) } + socketPath := filepath.Join(beadsDir, "bd.sock") client, err := rpc.TryConnect(socketPath) if err != nil { @@ -159,23 +143,13 @@ func showDaemonHealth(global bool) { } // showDaemonMetrics displays daemon metrics -func showDaemonMetrics(global bool) { - var socketPath string - if global { - home, err := os.UserHomeDir() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: cannot get home directory: %v\n", err) - os.Exit(1) - } - socketPath = filepath.Join(home, ".beads", "bd.sock") - } else { - beadsDir, err := ensureBeadsDir() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - socketPath = filepath.Join(beadsDir, "bd.sock") +func showDaemonMetrics() { + beadsDir, err := ensureBeadsDir() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) } + socketPath := filepath.Join(beadsDir, "bd.sock") client, err := rpc.TryConnect(socketPath) if err != nil { @@ -242,71 +216,6 @@ func showDaemonMetrics(global bool) { } } -// migrateToGlobalDaemon migrates from local to global daemon -func migrateToGlobalDaemon() { - home, err := os.UserHomeDir() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: cannot get home directory: %v\n", err) - os.Exit(1) - } - - localPIDFile := filepath.Join(".beads", "daemon.pid") - globalPIDFile := filepath.Join(home, ".beads", "daemon.pid") - - // Check if local daemon is running - localRunning, localPID := isDaemonRunning(localPIDFile) - if !localRunning { - fmt.Println("No local daemon is running") - } else { - fmt.Printf("Stopping local daemon (PID %d)...\n", localPID) - stopDaemon(localPIDFile) - } - - // Check if global daemon is already running - globalRunning, globalPID := isDaemonRunning(globalPIDFile) - if globalRunning { - fmt.Printf("Global daemon already running (PID %d)\n", globalPID) - return - } - - // Start global daemon - fmt.Println("Starting global daemon...") - binPath, err := os.Executable() - if err != nil { - binPath = os.Args[0] - } - - cmd := exec.Command(binPath, "daemon", "--global") // #nosec G204 - bd daemon command from trusted binary - devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, 0) - if err == nil { - cmd.Stdout = devNull - cmd.Stderr = devNull - cmd.Stdin = devNull - defer func() { _ = devNull.Close() }() - } - - configureDaemonProcess(cmd) - if err := cmd.Start(); err != nil { - fmt.Fprintf(os.Stderr, "Error: failed to start global daemon: %v\n", err) - os.Exit(1) - } - - go func() { _ = cmd.Wait() }() - - // Wait for daemon to be ready - time.Sleep(2 * time.Second) - - if isRunning, pid := isDaemonRunning(globalPIDFile); isRunning { - fmt.Printf("Global daemon started successfully (PID %d)\n", pid) - fmt.Println() - fmt.Println("Migration complete! The global daemon will now serve all your beads repositories.") - fmt.Println("Set BEADS_PREFER_GLOBAL_DAEMON=1 in your shell to make this permanent.") - } else { - fmt.Fprintf(os.Stderr, "Error: global daemon failed to start\n") - os.Exit(1) - } -} - // stopDaemon stops a running daemon func stopDaemon(pidFile string) { isRunning, pid := isDaemonRunning(pidFile) @@ -344,9 +253,7 @@ func stopDaemon(pidFile string) { return } - // Determine if this is global or local daemon - isGlobal := strings.Contains(pidFile, filepath.Join(".beads", "daemon.lock")) - socketPath := getSocketPathForPID(pidFile, isGlobal) + socketPath := getSocketPathForPID(pidFile) if err := process.Kill(); err != nil { // Ignore "process already finished" errors @@ -369,15 +276,15 @@ func stopDaemon(pidFile string) { } // startDaemon starts the daemon in background -func startDaemon(interval time.Duration, autoCommit, autoPush bool, logFile, pidFile string, global bool) { - logPath, err := getLogFilePath(logFile, global) +func startDaemon(interval time.Duration, autoCommit, autoPush bool, logFile, pidFile string) { + logPath, err := getLogFilePath(logFile) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } if os.Getenv("BD_DAEMON_FOREGROUND") == "1" { - runDaemonLoop(interval, autoCommit, autoPush, logPath, pidFile, global) + runDaemonLoop(interval, autoCommit, autoPush, logPath, pidFile) return } @@ -399,9 +306,6 @@ func startDaemon(interval time.Duration, autoCommit, autoPush bool, logFile, pid if logFile != "" { args = append(args, "--log", logFile) } - if global { - args = append(args, "--global") - } cmd := exec.Command(exe, args...) // #nosec G204 - bd daemon command from trusted binary cmd.Env = append(os.Environ(), "BD_DAEMON_FOREGROUND=1") diff --git a/cmd/bd/daemon_server.go b/cmd/bd/daemon_server.go index df50ec0b..9ad4f8fc 100644 --- a/cmd/bd/daemon_server.go +++ b/cmd/bd/daemon_server.go @@ -4,7 +4,6 @@ import ( "context" "os" "os/signal" - "path/filepath" "time" "github.com/steveyegge/beads/internal/rpc" @@ -40,39 +39,6 @@ func startRPCServer(ctx context.Context, socketPath string, store storage.Storag return server, serverErrChan, nil } -// runGlobalDaemon runs the global routing daemon -func runGlobalDaemon(ctx context.Context, log daemonLogger) { - globalDir, err := getGlobalBeadsDir() - if err != nil { - log.log("Error: cannot get global beads directory: %v", err) - os.Exit(1) - } - socketPath := filepath.Join(globalDir, "bd.sock") - - serverCtx, cancel := context.WithCancel(ctx) - defer cancel() - - server, _, err := startRPCServer(serverCtx, socketPath, nil, globalDir, "", log) - if err != nil { - return - } - - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, daemonSignals...) - defer signal.Stop(sigChan) - - sig := <-sigChan - log.log("Received signal: %v", sig) - log.log("Shutting down global daemon...") - - cancel() - if err := server.Stop(); err != nil { - log.log("Error stopping server: %v", err) - } - - log.log("Global daemon stopped") -} - // checkParentProcessAlive checks if the parent process is still running. // Returns true if parent is alive, false if it died. // Returns true if parent PID is 0 or 1 (not tracked, or adopted by init).