cmd/tailscaled: defer COM initialization and enabling GP lock restrictions on Windows until main runs

In this PR, we use a `tailscaledInit lazy.DeferredInit` to defer COM initialization and GP lock restriction
until the main function is called and determines that the current process is an actual tailscaled process,
rather than a CLI symlink, `tailscaled --version`, or another subcommand that doesn't require full initialization.

Updates #cleanup
Updates #14416

Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
Nick Khyl 2025-01-23 15:15:00 -06:00
parent f0db47338e
commit ba058bce86
No known key found for this signature in database
2 changed files with 36 additions and 19 deletions

View File

@ -59,6 +59,7 @@ import (
"tailscale.com/tsd"
"tailscale.com/tsweb/varz"
"tailscale.com/types/flagtype"
"tailscale.com/types/lazy"
"tailscale.com/types/logger"
"tailscale.com/types/logid"
"tailscale.com/util/clientmetric"
@ -153,6 +154,14 @@ var subCommands = map[string]*func([]string) error{
var beCLI func() // non-nil if CLI is linked in
// tailscaledInit facilitates the lazy initialization of tailscaled.
// It defers potentially expensive and platform-specific
// initialization steps until the main function is called and
// determines that the current process is an actual tailscaled
// process, rather than a CLI symlink, `tailscaled --version`, or
// another subcommand that doesn't require full initialization.
var tailscaledInit lazy.DeferredInit
func main() {
envknob.PanicIfAnyEnvCheckedInInit()
envknob.ApplyDiskConfig()
@ -227,6 +236,11 @@ func main() {
log.Fatalf("--bird-socket is not supported on %s", runtime.GOOS)
}
if err := tailscaledInit.Do(); err != nil {
log.SetFlags(0)
log.Fatalf("tailscaled init failed: %v", err)
}
// Only apply a default statepath when neither have been provided, so that a
// user may specify only --statedir if they wish.
if args.statepath == "" && args.statedir == "" {

View File

@ -60,31 +60,34 @@ import (
"tailscale.com/wf"
)
func init() {
// Initialize COM process-wide.
comProcessType := com.Service
if !isWindowsService() {
comProcessType = com.ConsoleApp
}
if err := com.StartRuntime(comProcessType); err != nil {
log.Printf("wingoes.com.StartRuntime(%d) failed: %v", comProcessType, err)
}
}
// permitPolicyLocks is a function to be called to lift the restriction on acquiring
// [gp.PolicyLock]s once the service is running.
// It is safe to be called multiple times.
var permitPolicyLocks = func() {}
func init() {
if isWindowsService() {
// We prevent [gp.PolicyLock]s from being acquired until the service enters the running state.
// Otherwise, if tailscaled starts due to a GPSI policy installing Tailscale, it may deadlock
// while waiting for the write counterpart of the GP lock to be released by Group Policy,
// which is itself waiting for the installation to complete and tailscaled to start.
// See tailscale/tailscale#14416 for more information.
permitPolicyLocks = gp.RestrictPolicyLocks()
}
tailscaledInit.Defer(func() error {
// Initialize COM process-wide.
comProcessType := com.Service
isService := isWindowsService()
if !isService {
comProcessType = com.ConsoleApp
}
if err := com.StartRuntime(comProcessType); err != nil {
return errors.New(fmt.Sprintf("wingoes.com.StartRuntime(%d) failed: %v", comProcessType, err))
}
if isService {
// We prevent [gp.PolicyLock]s from being acquired until the service enters the running state.
// Otherwise, if tailscaled starts due to a GPSI policy installing Tailscale, it may deadlock
// while waiting for the write counterpart of the GP lock to be released by Group Policy,
// which is itself waiting for the installation to complete and tailscaled to start.
// See tailscale/tailscale#14416 for more information.
permitPolicyLocks = gp.RestrictPolicyLocks()
}
return nil
})
}
const serviceName = "Tailscale"