priv: a failed root probe is retried, not cached for the life of the daemon
The Mac mini's daemon started while the machine was still booting, sudo -n true took longer than the 3 s timeout, and sync.Once kept the answer: no root, so the next self-update could not replace the binary. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
43bf787696
commit
468f45251d
+17
-3
@@ -13,12 +13,24 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
once sync.Once
|
mu sync.Mutex
|
||||||
mode string // "root" | "sudo" | "doas" | ""
|
mode string // "root" | "sudo" | "doas" | ""
|
||||||
prefix []string
|
prefix []string
|
||||||
|
probed time.Time
|
||||||
|
reprobe = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// detect settles how root is reached. Finding a way is remembered for good; finding none
|
||||||
|
// is only believed for a while, because the probe can fail for reasons that pass — sudo
|
||||||
|
// taking longer than the timeout on a machine that is still booting left a daemon
|
||||||
|
// convinced it had no root until its next restart.
|
||||||
func detect() {
|
func detect() {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
if mode != "" || (!probed.IsZero() && time.Since(probed) < reprobe) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
probed = time.Now()
|
||||||
if os.Geteuid() == 0 {
|
if os.Geteuid() == 0 {
|
||||||
mode = "root"
|
mode = "root"
|
||||||
return
|
return
|
||||||
@@ -38,7 +50,7 @@ func detect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mode reports how root is reached: "root", "sudo", "doas", or "" when it can't be.
|
// Mode reports how root is reached: "root", "sudo", "doas", or "" when it can't be.
|
||||||
func Mode() string { once.Do(detect); return mode }
|
func Mode() string { detect(); mu.Lock(); defer mu.Unlock(); return mode }
|
||||||
|
|
||||||
// Available reports whether commands can be run as root without a prompt.
|
// Available reports whether commands can be run as root without a prompt.
|
||||||
func Available() bool { return Mode() != "" }
|
func Available() bool { return Mode() != "" }
|
||||||
@@ -46,7 +58,9 @@ func Available() bool { return Mode() != "" }
|
|||||||
// Wrap returns args prefixed so they run as root. Without privilege it returns args
|
// Wrap returns args prefixed so they run as root. Without privilege it returns args
|
||||||
// unchanged; callers gate on Available() where that matters.
|
// unchanged; callers gate on Available() where that matters.
|
||||||
func Wrap(args ...string) []string {
|
func Wrap(args ...string) []string {
|
||||||
once.Do(detect)
|
detect()
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
return append(append([]string{}, prefix...), args...)
|
return append(append([]string{}, prefix...), args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user