File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -5,10 +5,10 @@ import (
55 "errors"
66 "fmt"
77 "os"
8- "os/exec"
98
109 "github.com/urfave/cli/v3"
1110
11+ "github.com/89luca89/distrobox/internal/rootful"
1212 "github.com/89luca89/distrobox/pkg/config"
1313 "github.com/89luca89/distrobox/pkg/containermanager"
1414 "github.com/89luca89/distrobox/pkg/containermanager/providers"
@@ -55,19 +55,6 @@ func printInvalidContainerManager(p *ui.Printer, containerManagerType string) {
5555 p .Println ("The available choices are: 'autodetect', 'podman', 'podman-launcher', 'docker'" )
5656}
5757
58- func validateSudo (ctx context.Context ) error {
59- cmd := exec .CommandContext (ctx , "sudo" , "-v" )
60- cmd .Stdin = os .Stdin
61- cmd .Stdout = os .Stdout
62- cmd .Stderr = os .Stderr
63-
64- if err := cmd .Run (); err != nil {
65- return fmt .Errorf ("failed to validate sudo: %w" , err )
66- }
67-
68- return nil
69- }
70-
7158func subcommands (cfg * config.Values ) []* cli.Command {
7259 return []* cli.Command {
7360 composeCommand (
@@ -152,7 +139,7 @@ func withRoot(_ *config.Values, cmd *cli.Command) *cli.Command {
152139 }
153140 }
154141 if c .Bool ("root" ) {
155- if err := validateSudo (ctx ); err != nil {
142+ if err := rootful . Validate (ctx ); err != nil {
156143 return nil , fmt .Errorf ("cannot run in root mode: %w" , err )
157144 }
158145 }
Original file line number Diff line number Diff line change 1+ // Package rootful provides utilities for running operations that require
2+ // root privileges via sudo.
3+ package rootful
4+
5+ import (
6+ "context"
7+ "fmt"
8+ "os"
9+ "os/exec"
10+ "sync"
11+ )
12+
13+ //nolint:gochecknoglobals // singleton: process-wide memoization is the intent
14+ var (
15+ validateOnce sync.Once
16+ errValidate error
17+ )
18+
19+ // Validate ensures that sudo is available and the user can elevate.
20+ // It runs `sudo -v` at most once per process: the first call performs the
21+ // check and caches the result; subsequent calls return the cached result
22+ // without re-running the command.
23+ func Validate (ctx context.Context ) error {
24+ validateOnce .Do (func () {
25+ cmd := exec .CommandContext (ctx , "sudo" , "-v" )
26+ cmd .Stdin = os .Stdin
27+ cmd .Stdout = os .Stdout
28+ cmd .Stderr = os .Stderr
29+ if err := cmd .Run (); err != nil {
30+ errValidate = fmt .Errorf ("failed to validate sudo: %w" , err )
31+ }
32+ })
33+ return errValidate
34+ }
You can’t perform that action at this time.
0 commit comments