diff --git a/go.mod b/go.mod index c6db6a2c2..fd78cd439 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/codefly-dev/authority/libraries/authority-core/go v0.0.0-20261026143932-67b85b14a942 github.com/codefly-dev/authority/libraries/delivery/go v0.0.0-20261026023425-000d44495d93 github.com/codefly-dev/authority/libraries/hop/go v0.0.0-20261026175541-3afad7224855 - github.com/codefly-dev/core v0.19.7-0.20261026150808-7c0407679119 + github.com/codefly-dev/core v0.19.7-0.20261026194550-a8599bd3991a github.com/codefly-dev/golor v0.1.3 github.com/codefly-dev/llm v0.1.7 github.com/codefly-dev/sdk-go/workcontext v0.1.1-0.20261018052022-70dc7a546458 diff --git a/go.sum b/go.sum index 979528e27..1b8b7e3f7 100644 --- a/go.sum +++ b/go.sum @@ -109,14 +109,12 @@ github.com/codefly-dev/authority/libraries/authority-core/go v0.0.0-202610261439 github.com/codefly-dev/authority/libraries/authority-core/go v0.0.0-20261026143932-67b85b14a942/go.mod h1:n+JVNRyv2dp7BIwUoKYn3TcjN7YpZmxMhBrWp14wsKQ= github.com/codefly-dev/authority/libraries/delivery/go v0.0.0-20261026023425-000d44495d93 h1:PnhgA9wkib67bJnc5hkhkc80J2ch5uYiq+9/NiD/XFU= github.com/codefly-dev/authority/libraries/delivery/go v0.0.0-20261026023425-000d44495d93/go.mod h1:XsOFnO2MQdr37vM3ULa0Uwi8ZWptyVx7hRSUyIdaZ1Q= -github.com/codefly-dev/authority/libraries/hop/go v0.0.0-20261025214748-c9cef7470d20 h1:zokkjvY+C+FhXq9Tuo93wJBVwLxcX4RfHy8U8KeAAW8= -github.com/codefly-dev/authority/libraries/hop/go v0.0.0-20261025214748-c9cef7470d20/go.mod h1:021MSgdhW9pSlRLcHEryPDXfI/rGY49rJAhe4RbU7BQ= github.com/codefly-dev/authority/libraries/hop/go v0.0.0-20261026175541-3afad7224855 h1:XmPltOHirB97RopD8WIJe9bwt1wkHAzngKoj8k2XI8U= github.com/codefly-dev/authority/libraries/hop/go v0.0.0-20261026175541-3afad7224855/go.mod h1:rfn41uiORQ36RrOeFEjXHPLC9NUi0j14AoYJtcovsn4= github.com/codefly-dev/authority/libraries/resources/go v0.0.0-20261026023425-000d44495d93 h1:4EygH7/J0E6aM7hUPdbQGfit1rsLUNCtWJ8kMh2b9PE= github.com/codefly-dev/authority/libraries/resources/go v0.0.0-20261026023425-000d44495d93/go.mod h1:1rmjrVyr0h7zOuwOHS9i+BoeS9dr3XNvtupLZFwY6As= -github.com/codefly-dev/core v0.19.7-0.20261026150808-7c0407679119 h1:+Gkwdvx1g0xla5oyHe0OXBtT0NA2E2BqjfGX7r6uhy8= -github.com/codefly-dev/core v0.19.7-0.20261026150808-7c0407679119/go.mod h1:1ina3kP+YSAL4tlZvnEYd4P8bFGnJECfkP86ojeeF1A= +github.com/codefly-dev/core v0.19.7-0.20261026194550-a8599bd3991a h1:ar8ERpN9n9ZL29LZ63mqBZNLPZ7OpcnQ1+D1g89DbPY= +github.com/codefly-dev/core v0.19.7-0.20261026194550-a8599bd3991a/go.mod h1:1ina3kP+YSAL4tlZvnEYd4P8bFGnJECfkP86ojeeF1A= github.com/codefly-dev/golor v0.1.3 h1:xmo+ceyJFRYZdvpWE2fNd0jeaadp/Ibm1BnganiGKOc= github.com/codefly-dev/golor v0.1.3/go.mod h1:sl/u/K1l7J0Pr3xyVZp8fOJYQItKKst1No9JqgzLLoY= github.com/codefly-dev/gortk v0.2.0 h1:7bOlS5valYz2zil+fZctQNcPCYBcPj86abcw9N8h1hQ= diff --git a/pkg/processgroup/identity_darwin.go b/pkg/processgroup/identity_darwin.go index 1126a71ec..2115fc9a4 100644 --- a/pkg/processgroup/identity_darwin.go +++ b/pkg/processgroup/identity_darwin.go @@ -6,8 +6,8 @@ import ( "bytes" "encoding/binary" "errors" - "fmt" "runtime" + "strings" "syscall" "unsafe" @@ -113,12 +113,30 @@ func processWorkingDirectory(pid int) (string, error) { return string(path[:end]), nil } +// darwinBootID names the running boot, and has to agree with the boot id Core +// writes into the records this package reads (runners/base, darwinBootID). +// +// It has to hold still for as long as the machine stays up. Every recorded +// identity is compared against it, and a record whose boot id no longer matches +// is dropped as a reused group without signaling it, so a boot id that moves +// strands every live group on the machine at once: none of them can be reaped +// after their owner dies. +// +// kern.boottime is such a value. It is not a timestamp taken at boot but the +// wall clock minus the uptime, and the kernel re-derives it whenever the clock +// is set (xnu's clock_set_calendar_microtime "also adjusts the boottime to keep +// the value consistent"). timed steps the clock several times a day. +// kern.bootsessionuuid is minted once per boot and never rewritten. func darwinBootID() (string, error) { - bootTime, err := unix.SysctlTimeval("kern.boottime") + session, err := unix.Sysctl("kern.bootsessionuuid") if err != nil { return "", err } - return fmt.Sprintf("%d:%d", bootTime.Sec, bootTime.Usec), nil + bootID := strings.TrimSpace(session) + if bootID == "" { + return "", errors.New("boot identity is empty") + } + return bootID, nil } // readProcessEnvironmentValue returns the value pid carries for key in its diff --git a/pkg/processgroup/identity_darwin_test.go b/pkg/processgroup/identity_darwin_test.go new file mode 100644 index 000000000..d025fb071 --- /dev/null +++ b/pkg/processgroup/identity_darwin_test.go @@ -0,0 +1,51 @@ +//go:build darwin + +package processgroup + +import ( + "fmt" + "os" + "strings" + "testing" + + "golang.org/x/sys/unix" +) + +// TestDarwinBootIDNamesTheBootSessionNotTheClock pins the boot id to the one +// Darwin value that holds still for a whole boot. kern.boottime moves every +// time the clock is set, and a boot id built from it made every live record +// stop matching its own process: the reaper dropped the record as a reused +// group and the group could never be reaped again. +func TestDarwinBootIDNamesTheBootSessionNotTheClock(t *testing.T) { + session, err := unix.Sysctl("kern.bootsessionuuid") + if err != nil { + t.Fatal(err) + } + bootID, err := darwinBootID() + if err != nil { + t.Fatal(err) + } + if bootID != session { + t.Fatalf("boot id = %q, want the boot session %q", bootID, session) + } + bootTime, err := unix.SysctlTimeval("kern.boottime") + if err != nil { + t.Fatal(err) + } + for _, clockDerived := range []string{ + fmt.Sprintf("%d:%d", bootTime.Sec, bootTime.Usec), + fmt.Sprintf("%d", bootTime.Sec), + } { + if strings.Contains(bootID, clockDerived) { + t.Fatalf("boot id %q is derived from kern.boottime %q, which the kernel rewrites when the clock is set", bootID, clockDerived) + } + } + + identity, err := inspectNativeProcess(os.Getpid()) + if err != nil { + t.Fatal(err) + } + if identity.bootID != session { + t.Fatalf("inspected boot id = %q, want the boot session %q", identity.bootID, session) + } +}