feature/sdnotify: move util/systemd to a modular feature

Updates #12614

Change-Id: I08e714c83b455df7f538cc99cafe940db936b480
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-09-27 19:28:35 -07:00
committed by Brad Fitzpatrick
parent 7bcab4ab28
commit 976389c0f7
18 changed files with 98 additions and 34 deletions

View File

@@ -88,6 +88,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
tailscale.com/drive from tailscale.com/client/local+
tailscale.com/envknob from tailscale.com/client/local+
tailscale.com/feature from tailscale.com/tsweb+
tailscale.com/feature/buildfeatures from tailscale.com/feature
tailscale.com/health from tailscale.com/net/tlsdial+
tailscale.com/hostinfo from tailscale.com/net/netmon+
tailscale.com/ipn from tailscale.com/client/local

View File

@@ -164,7 +164,6 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
L 💣 github.com/mdlayher/netlink from github.com/google/nftables+
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
L github.com/mdlayher/netlink/nltest from github.com/google/nftables
L github.com/mdlayher/sdnotify from tailscale.com/util/systemd
L 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink+
💣 github.com/mitchellh/go-ps from tailscale.com/safesocket
github.com/modern-go/concurrent from github.com/json-iterator/go
@@ -957,7 +956,6 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy+
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
tailscale.com/util/systemd from tailscale.com/control/controlclient+
tailscale.com/util/testenv from tailscale.com/control/controlclient+
tailscale.com/util/truncate from tailscale.com/logtail
tailscale.com/util/usermetric from tailscale.com/health+

View File

@@ -51,6 +51,7 @@ tailscale.com/cmd/stund dependencies: (generated by github.com/tailscale/depawar
tailscale.com from tailscale.com/version
tailscale.com/envknob from tailscale.com/tsweb+
tailscale.com/feature from tailscale.com/tsweb
tailscale.com/feature/buildfeatures from tailscale.com/feature
tailscale.com/kube/kubetypes from tailscale.com/envknob
tailscale.com/metrics from tailscale.com/net/stunserver+
tailscale.com/net/netaddr from tailscale.com/net/tsaddr

View File

@@ -33,7 +33,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
💣 github.com/mdlayher/netlink from github.com/google/nftables+
💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
github.com/mdlayher/netlink/nltest from github.com/google/nftables
github.com/mdlayher/sdnotify from tailscale.com/util/systemd
💣 github.com/mdlayher/socket from github.com/mdlayher/netlink+
💣 github.com/safchain/ethtool from tailscale.com/net/netkernelconf
github.com/tailscale/hujson from tailscale.com/ipn/conffile
@@ -202,7 +201,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/util/syspolicy/pkey from tailscale.com/cmd/tailscaled+
tailscale.com/util/syspolicy/policyclient from tailscale.com/cmd/tailscaled+
tailscale.com/util/syspolicy/ptype from tailscale.com/ipn/ipnlocal+
tailscale.com/util/systemd from tailscale.com/control/controlclient+
tailscale.com/util/testenv from tailscale.com/control/controlclient+
tailscale.com/util/usermetric from tailscale.com/health+
tailscale.com/util/vizerror from tailscale.com/tailcfg+

View File

@@ -147,7 +147,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L 💣 github.com/mdlayher/netlink from github.com/google/nftables+
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
L github.com/mdlayher/netlink/nltest from github.com/google/nftables
L github.com/mdlayher/sdnotify from tailscale.com/util/systemd
L github.com/mdlayher/sdnotify from tailscale.com/feature/sdnotify
L 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink+
💣 github.com/mitchellh/go-ps from tailscale.com/safesocket
L github.com/pierrec/lz4/v4 from github.com/u-root/uio/uio
@@ -282,6 +282,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/feature/portlist from tailscale.com/feature/condregister
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
tailscale.com/feature/relayserver from tailscale.com/feature/condregister
L tailscale.com/feature/sdnotify from tailscale.com/feature/condregister
tailscale.com/feature/syspolicy from tailscale.com/feature/condregister+
tailscale.com/feature/taildrop from tailscale.com/feature/condregister
L tailscale.com/feature/tap from tailscale.com/feature/condregister
@@ -446,7 +447,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy+
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
tailscale.com/util/systemd from tailscale.com/control/controlclient+
tailscale.com/util/testenv from tailscale.com/ipn/ipnlocal+
tailscale.com/util/truncate from tailscale.com/logtail
tailscale.com/util/usermetric from tailscale.com/health+

View File

@@ -128,7 +128,6 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
L 💣 github.com/mdlayher/netlink from github.com/google/nftables+
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
L github.com/mdlayher/netlink/nltest from github.com/google/nftables
L github.com/mdlayher/sdnotify from tailscale.com/util/systemd
L 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink+
💣 github.com/mitchellh/go-ps from tailscale.com/safesocket
D github.com/prometheus-community/pro-bing from tailscale.com/wgengine/netstack
@@ -387,7 +386,6 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
tailscale.com/util/syspolicy/rsop from tailscale.com/ipn/localapi+
tailscale.com/util/syspolicy/setting from tailscale.com/client/local+
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
tailscale.com/util/systemd from tailscale.com/control/controlclient+
tailscale.com/util/testenv from tailscale.com/control/controlclient+
tailscale.com/util/truncate from tailscale.com/logtail
tailscale.com/util/usermetric from tailscale.com/health+

View File

@@ -29,6 +29,7 @@ import (
"go4.org/mem"
"tailscale.com/control/controlknobs"
"tailscale.com/envknob"
"tailscale.com/feature"
"tailscale.com/health"
"tailscale.com/hostinfo"
"tailscale.com/ipn/ipnstate"
@@ -57,7 +58,6 @@ import (
"tailscale.com/util/singleflight"
"tailscale.com/util/syspolicy/pkey"
"tailscale.com/util/syspolicy/policyclient"
"tailscale.com/util/systemd"
"tailscale.com/util/testenv"
"tailscale.com/util/zstdframe"
)
@@ -543,7 +543,9 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
} else {
if expired {
c.logf("Old key expired -> regen=true")
systemd.Status("key expired; run 'tailscale up' to authenticate")
if f, ok := feature.HookSystemdStatus.GetOk(); ok {
f("key expired; run 'tailscale up' to authenticate")
}
regen = true
}
if (opt.Flags & LoginInteractive) != 0 {

View File

@@ -0,0 +1,13 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Code generated by gen.go; DO NOT EDIT.
//go:build ts_omit_sdnotify
package buildfeatures
// HasSDNotify is whether the binary was built with support for modular feature "systemd notification support".
// Specifically, it's whether the binary was NOT built with the "ts_omit_sdnotify" build tag.
// It's a const so it can be used for dead code elimination.
const HasSDNotify = false

View File

@@ -0,0 +1,13 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Code generated by gen.go; DO NOT EDIT.
//go:build !ts_omit_sdnotify
package buildfeatures
// HasSDNotify is whether the binary was built with support for modular feature "systemd notification support".
// Specifically, it's whether the binary was NOT built with the "ts_omit_sdnotify" build tag.
// It's a const so it can be used for dead code elimination.
const HasSDNotify = true

View File

@@ -0,0 +1,8 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux && !ts_omit_sdnotify
package condregister
import _ "tailscale.com/feature/sdnotify"

View File

@@ -145,6 +145,10 @@ var Features = map[FeatureTag]FeatureMeta{
Desc: "Linux systemd-resolved integration",
Deps: []FeatureTag{"dbus"},
},
"sdnotify": {
Sym: "SDNotify",
Desc: "systemd notification support",
},
"serve": {
Sym: "Serve",
Desc: "Serve and Funnel support",

32
feature/sdnotify.go Normal file
View File

@@ -0,0 +1,32 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package feature
import (
"runtime"
"tailscale.com/feature/buildfeatures"
)
// HookSystemdReady sends a readiness to systemd. This will unblock service
// dependents from starting.
var HookSystemdReady Hook[func()]
// HookSystemdStatus holds a func that will send a single line status update to
// systemd so that information shows up in systemctl output.
var HookSystemdStatus Hook[func(format string, args ...any)]
// SystemdStatus sends a single line status update to systemd so that
// information shows up in systemctl output.
//
// It does nothing on non-Linux systems or if the binary was built without
// the sdnotify feature.
func SystemdStatus(format string, args ...any) {
if runtime.GOOS != "linux" || !buildfeatures.HasSDNotify {
return
}
if f, ok := HookSystemdStatus.GetOk(); ok {
f(format, args...)
}
}

View File

@@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
Package systemd contains a minimal wrapper around systemd-notify to enable
Package sdnotify contains a minimal wrapper around systemd-notify to enable
applications to signal readiness and status to systemd.
This package will only have effect on Linux systems running Tailscale in a
@@ -10,4 +10,4 @@ systemd unit with the Type=notify flag set. On other operating systems (or
when running in a Linux distro without being run from inside systemd) this
package will become a no-op.
*/
package systemd
package sdnotify

View File

@@ -3,7 +3,7 @@
//go:build linux && !android
package systemd
package sdnotify
import (
"errors"
@@ -12,8 +12,14 @@ import (
"sync"
"github.com/mdlayher/sdnotify"
"tailscale.com/feature"
)
func init() {
feature.HookSystemdReady.Set(ready)
feature.HookSystemdStatus.Set(status)
}
var getNotifyOnce struct {
sync.Once
v *sdnotify.Notifier
@@ -46,15 +52,15 @@ func notifier() *sdnotify.Notifier {
return getNotifyOnce.v
}
// Ready signals readiness to systemd. This will unblock service dependents from starting.
func Ready() {
// ready signals readiness to systemd. This will unblock service dependents from starting.
func ready() {
err := notifier().Notify(sdnotify.Ready)
if err != nil {
readyOnce.logf("systemd: error notifying: %v", err)
}
}
// Status sends a single line status update to systemd so that information shows up
// status sends a single line status update to systemd so that information shows up
// in systemctl output. For example:
//
// $ systemctl status tailscale
@@ -69,7 +75,7 @@ func Ready() {
// CPU: 2min 38.469s
// CGroup: /system.slice/tailscale.service
// └─26741 /nix/store/sv6cj4mw2jajm9xkbwj07k29dj30lh0n-tailscale-date.20200727/bin/tailscaled --port 41641
func Status(format string, args ...any) {
func status(format string, args ...any) {
err := notifier().Notify(sdnotify.Statusf(format, args...))
if err != nil {
statusOnce.logf("systemd: error notifying: %v", err)

View File

@@ -102,7 +102,6 @@ import (
"tailscale.com/util/syspolicy/pkey"
"tailscale.com/util/syspolicy/policyclient"
"tailscale.com/util/syspolicy/ptype"
"tailscale.com/util/systemd"
"tailscale.com/util/testenv"
"tailscale.com/util/usermetric"
"tailscale.com/version"
@@ -5488,7 +5487,7 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State, unlock unlock
switch newState {
case ipn.NeedsLogin:
systemd.Status("Needs login: %s", authURL)
feature.SystemdStatus("Needs login: %s", authURL)
// always block updates on NeedsLogin even if seamless renewal is enabled,
// to prevent calls to authReconfig from reconfiguring the engine when our
// key has expired and we're waiting to authenticate to use the new key.
@@ -5503,7 +5502,7 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State, unlock unlock
}
if newState == ipn.Stopped && authURL == "" {
systemd.Status("Stopped; run 'tailscale up' to log in")
feature.SystemdStatus("Stopped; run 'tailscale up' to log in")
}
case ipn.Starting, ipn.NeedsMachineAuth:
b.authReconfig()
@@ -5515,7 +5514,7 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State, unlock unlock
for _, p := range addrs.All() {
addrStrs = append(addrStrs, p.Addr().String())
}
systemd.Status("Connected; %s; %s", activeLogin, strings.Join(addrStrs, " "))
feature.SystemdStatus("Connected; %s; %s", activeLogin, strings.Join(addrStrs, " "))
default:
b.logf("[unexpected] unknown newState %#v", newState)
}

View File

@@ -23,6 +23,7 @@ import (
"tailscale.com/client/tailscale/apitype"
"tailscale.com/envknob"
"tailscale.com/feature"
"tailscale.com/ipn/ipnauth"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/localapi"
@@ -32,7 +33,6 @@ import (
"tailscale.com/util/eventbus"
"tailscale.com/util/mak"
"tailscale.com/util/set"
"tailscale.com/util/systemd"
"tailscale.com/util/testenv"
)
@@ -513,7 +513,9 @@ func (s *Server) Run(ctx context.Context, ln net.Listener) error {
ln.Close()
}()
systemd.Ready()
if ready, ok := feature.HookSystemdReady.GetOk(); ok {
ready()
}
hs := &http.Server{
Handler: http.HandlerFunc(s.serveHTTP),

View File

@@ -128,7 +128,6 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
L 💣 github.com/mdlayher/netlink from github.com/google/nftables+
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
L github.com/mdlayher/netlink/nltest from github.com/google/nftables
L github.com/mdlayher/sdnotify from tailscale.com/util/systemd
LA 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink+
LDW 💣 github.com/mitchellh/go-ps from tailscale.com/safesocket
DI github.com/prometheus-community/pro-bing from tailscale.com/wgengine/netstack
@@ -382,7 +381,6 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
tailscale.com/util/syspolicy/rsop from tailscale.com/ipn/localapi+
tailscale.com/util/syspolicy/setting from tailscale.com/client/local+
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
tailscale.com/util/systemd from tailscale.com/control/controlclient+
tailscale.com/util/testenv from tailscale.com/control/controlclient+
tailscale.com/util/truncate from tailscale.com/logtail
tailscale.com/util/usermetric from tailscale.com/health+

View File

@@ -1,9 +0,0 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !linux || android
package systemd
func Ready() {}
func Status(string, ...any) {}