diff --git a/cmd/tailscaled/tailscaled.go b/cmd/tailscaled/tailscaled.go index a760db708..5c2f0f9cc 100644 --- a/cmd/tailscaled/tailscaled.go +++ b/cmd/tailscaled/tailscaled.go @@ -29,6 +29,7 @@ "time" "github.com/go-multierror/multierror" + "tailscale.com/ipn/ipnlocal" "tailscale.com/ipn/ipnserver" "tailscale.com/logpolicy" "tailscale.com/net/socks5" @@ -227,6 +228,8 @@ func run() error { return err } + localBEFuture := ipnlocal.NewLocalBackendFuture() + var ns *netstack.Impl if useNetstack { tunDev, magicConn := e.(wgengine.InternalsGetter).GetInternals() @@ -298,6 +301,7 @@ func run() error { LegacyConfigPath: paths.LegacyConfigPath(), SurviveDisconnects: true, DebugMux: debugMux, + OnBackendCreated: localBEFuture.Set, } err = ipnserver.Run(ctx, logf, pol.PublicID.String(), ipnserver.FixedEngine(e), opts) // Cancelation is not an error: it is the only way to stop ipnserver. diff --git a/ipn/ipnlocal/future.go b/ipn/ipnlocal/future.go new file mode 100644 index 000000000..bcefe8edd --- /dev/null +++ b/ipn/ipnlocal/future.go @@ -0,0 +1,28 @@ +// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipnlocal + +import "sync" + +// LocalBackendFuture is a Future that returns a *LocalBackend. +type LocalBackendFuture struct { + getOnce sync.Once + ch chan *LocalBackend + v *LocalBackend +} + +func (f *LocalBackendFuture) Get() *LocalBackend { + f.getOnce.Do(f.get) + return f.v +} + +func (f *LocalBackendFuture) get() { f.v = <-f.ch } +func (f *LocalBackendFuture) Set(v *LocalBackend) { f.ch <- v } + +func NewLocalBackendFuture() *LocalBackendFuture { + return &LocalBackendFuture{ + ch: make(chan *LocalBackend, 1), + } +} diff --git a/ipn/ipnserver/server.go b/ipn/ipnserver/server.go index 50b848506..2b74035ee 100644 --- a/ipn/ipnserver/server.go +++ b/ipn/ipnserver/server.go @@ -92,6 +92,10 @@ type Options struct { // DebugMux, if non-nil, specifies an HTTP ServeMux in which // to register a debug handler. DebugMux *http.ServeMux + + // OnBackendCreated, if non-nil, is called once when the LocalBackend + // is created. + OnBackendCreated func(*ipnlocal.LocalBackend) } // server is an IPN backend and its set of 0 or more active connections @@ -739,6 +743,10 @@ func Run(ctx context.Context, logf logger.Logf, logid string, getEngine func() ( return smallzstd.NewDecoder(nil) }) + if opts.OnBackendCreated != nil { + opts.OnBackendCreated(b) + } + if opts.DebugMux != nil { opts.DebugMux.HandleFunc("/debug/ipn", func(w http.ResponseWriter, r *http.Request) { serveHTMLStatus(w, b)