Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions daemon/sdnotify_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2025
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package daemon

import (
"strconv"
"strings"
"testing"
"time"

"golang.org/x/sys/unix"
)

// TestSdNotifyMonotonicUsec checks that SdNotifyMonotonicUsec is probably not returning complete garbage.
func TestSdNotifyMonotonicUsec(t *testing.T) {
var resolution unix.Timespec
if err := unix.ClockGetres(unix.CLOCK_MONOTONIC, &resolution); err != nil {
if err == unix.EINVAL {
t.Log("CLOCK_MONOTONIC is not supported on this system")
if got := SdNotifyMonotonicUsec(); got != "" {
t.Errorf("SdNotifyMonotonicUsec() = %q; want empty string", got)
}
return
}
t.Fatalf("ClockGetres(CLOCK_MONOTONIC) failed: %v", err)
}

now := func() uint64 {
got := SdNotifyMonotonicUsec()
t.Logf("SdNotifyMonotonicUsec() = %q", got)
if got == "" {
t.Fatal("SdNotifyMonotonicUsec() returned empty string on system which supports CLOCK_MONOTONIC")
}
fields := strings.SplitN(got, "=", 2)
if len(fields) != 2 {
t.Fatal("string is not a well-formed variable assignment")
}
tag, val := fields[0], fields[1]
if tag != "MONOTONIC_USEC" {
t.Errorf("expected tag MONOTONIC_USEC, got %q", tag)
}
if val[len(val)-1] != '\n' {
t.Errorf("expected value to end with newline, got %q", val)
}
ts, err := strconv.ParseUint(val[:len(val)-1], 10, 64)
if err != nil {
t.Fatalf("value %q is not well-formed: %v", val, err)
}
if ts == 0 {
// CLOCK_MONOTONIC is defined on Linux as the number of seconds
// since boot, per clock_gettime(2). A timestamp of zero is
// almost certainly bogus.
t.Fatal("timestamp is zero")
}
return ts
}

start := now()
time.Sleep(time.Duration(resolution.Nano()) * 3)
ts := now()
if ts < start {
t.Errorf("timestamp went backwards: %d < %d", ts, start)
} else if ts == start {
t.Errorf("timestamp did not advance: %d == %d", ts, start)
}
}
22 changes: 22 additions & 0 deletions daemon/sdnotify_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2025
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build !unix

package daemon

// SdNotifyMonotonicUsec returns the empty string on unsupported platforms.
func SdNotifyMonotonicUsec() string {
return ""
}
36 changes: 36 additions & 0 deletions daemon/sdnotify_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2025
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build unix

package daemon

import (
"strconv"

"golang.org/x/sys/unix"
)

// SdNotifyMonotonicUsec returns a MONOTONIC_USEC=... assignment for the current time
// with a trailing newline included. This is typically used with [SdNotifyReloading].
//
// If the monotonic clock is not available on the system, the empty string is returned.
func SdNotifyMonotonicUsec() string {
var ts unix.Timespec
if err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts); err != nil {
// Monotonic clock is not available on this system.
return ""
}
return "MONOTONIC_USEC=" + strconv.FormatInt(ts.Nano()/1000, 10) + "\n"
}
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module github.com/coreos/go-systemd/v22

go 1.23

require github.com/godbus/dbus/v5 v5.1.0
require (
github.com/godbus/dbus/v5 v5.1.0
golang.org/x/sys v0.1.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use something more recent now that we support go 1.23 as minimum version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modules are expected to declare requirements on the minimum version of dependencies. This module is compatible with golang.org/x/sys v0.1.0 or any later version. Declaring a dependency on a newer version than the minimum compatible only serves to force unnecessary dependency bumps on modules that require go-systemd.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well yes but also not really though, because that is also the exact version used by this module. So if there is a CVE in golang.org/x/sys then we would not use the newer version for testing. And any module importing go-systemd and not using golang.org/x/sys themselves directly or via other deps would also end up on the older version.

Does it matter right now? Properly not, the only CVE I see for golang.org/x/sys on https://pkg.go.dev/vuln/list was before 0.1.0 so this here is most likely fine. But it also means we miss out on potential other bug fixes in the meantime.

And if we already require go 1.23 then there seems little reason to keep it on such an old version.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that's out of scope for this module; the only CVE this module would care about is a CVE in unix.ClockGettime as that's the only code it depends on.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I was a bit terse in my previous comment; basically for this;

And any module importing go-systemd and not using golang.org/x/sys themselves directly or via other deps would also end up on the older version.

The MVS design is for each module to specify the lowest possible version of dependencies that's required to use the module. "lowest possible" can account for CVEs in the parts of the dependency; for this module that means; if there's a CVE or bug in unix.ClockGettime that impacts this module, then it can update the minimum required version accordingly.

But it's NOT this module's responsibility to "patch" other modules or codebases that happen to use this module as part of their codebase. If their code, or another module uses other parts of golang.org/x/sys that is impacted by a bug or CVE in golang.org/x/sys, then either the module itself, or the dependency that introduces golang.org/x/sys as a dependency can update the version.

Pre-emptively updating the version only causes code-churn for all consumers of this module; in this case even though this module only uses a single function from golang.org/x/sys, but would be enforcing all users to accept updating all other parts that may be used by them.

)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=