package cli import ( "context" "errors" "fmt" "os" "strings" "banger/internal/config" "banger/internal/installmeta" "banger/internal/model" "banger/internal/paths" ) var ( loadInstallMetadata = func() (installmeta.Metadata, error) { return installmeta.Load(installmeta.DefaultPath) } currentUID = os.Getuid ) // ensureDaemon validates that the current CLI user matches the // installed banger owner, then pings the system socket. Every CLI // command that needs to talk to the daemon routes through here. func (d *deps) ensureDaemon(ctx context.Context) (paths.Layout, model.DaemonConfig, error) { meta, metaErr := loadInstallMetadata() if metaErr == nil && currentUID() != meta.OwnerUID { return paths.Layout{}, model.DaemonConfig{}, fmt.Errorf("banger is installed for %s; switch to that user or reinstall with `sudo banger system install --owner %s`", meta.OwnerUser, userHint()) } if metaErr != nil && !errors.Is(metaErr, os.ErrNotExist) { return paths.Layout{}, model.DaemonConfig{}, fmt.Errorf("load %s: %w", installmeta.DefaultPath, metaErr) } userLayout, err := paths.Resolve() if err != nil { return paths.Layout{}, model.DaemonConfig{}, err } cfg, err := config.Load(userLayout) if err != nil { return paths.Layout{}, model.DaemonConfig{}, err } layout := paths.ResolveSystem() if _, err := d.daemonPing(ctx, layout.SocketPath); err == nil { return layout, cfg, nil } if metaErr == nil { return paths.Layout{}, model.DaemonConfig{}, fmt.Errorf("banger service not reachable at %s; run `sudo banger system restart`", layout.SocketPath) } return paths.Layout{}, model.DaemonConfig{}, fmt.Errorf("banger service not running at %s; run `sudo banger system install`", layout.SocketPath) } func userHint() string { if sudoUser := strings.TrimSpace(os.Getenv("SUDO_USER")); sudoUser != "" { return sudoUser } if user := strings.TrimSpace(os.Getenv("USER")); user != "" { return user } return "" }