diff --git a/cmd/cloner/cloner.go b/cmd/cloner/cloner.go index 098faaca1..73c9d2412 100644 --- a/cmd/cloner/cloner.go +++ b/cmd/cloner/cloner.go @@ -33,6 +33,7 @@ flagTypes = flag.String("type", "", "comma-separated list of types; required") flagOutput = flag.String("output", "", "output file; required") flagBuildTags = flag.String("tags", "", "compiler build tags to apply") + flagCloneFunc = flag.Bool("clonefunc", false, "add a top-level Clone func") ) func main() { @@ -98,25 +99,27 @@ func main() { w := func(format string, args ...interface{}) { fmt.Fprintf(buf, format+"\n", args...) } - w("// Clone duplicates src into dst and reports whether it succeeded.") - w("// To succeed, must be of types <*T, *T> or <*T, **T>,") - w("// where T is one of %s.", *flagTypes) - w("func Clone(dst, src interface{}) bool {") - w(" switch src := src.(type) {") - for _, typeName := range typeNames { - w(" case *%s:", typeName) - w(" switch dst := dst.(type) {") - w(" case *%s:", typeName) - w(" *dst = *src.Clone()") - w(" return true") - w(" case **%s:", typeName) - w(" *dst = src.Clone()") - w(" return true") - w(" }") + if *flagCloneFunc { + w("// Clone duplicates src into dst and reports whether it succeeded.") + w("// To succeed, must be of types <*T, *T> or <*T, **T>,") + w("// where T is one of %s.", *flagTypes) + w("func Clone(dst, src interface{}) bool {") + w(" switch src := src.(type) {") + for _, typeName := range typeNames { + w(" case *%s:", typeName) + w(" switch dst := dst.(type) {") + w(" case *%s:", typeName) + w(" *dst = *src.Clone()") + w(" return true") + w(" case **%s:", typeName) + w(" *dst = src.Clone()") + w(" return true") + w(" }") + } + w(" }") + w(" return false") + w("}") } - w(" }") - w(" return false") - w("}") contents := new(bytes.Buffer) fmt.Fprintf(contents, header, *flagTypes, pkg.Name) diff --git a/ipn/prefs.go b/ipn/prefs.go index 115ae1f58..8c1457e37 100644 --- a/ipn/prefs.go +++ b/ipn/prefs.go @@ -20,6 +20,8 @@ "tailscale.com/wgengine/router" ) +//go:generate go run tailscale.com/cmd/cloner -type=Prefs -output=prefs_clone.go + // Prefs are the user modifiable settings of the Tailscale node agent. type Prefs struct { // ControlURL is the URL of the control server to use. @@ -261,16 +263,6 @@ func PrefsFromBytes(b []byte, enforceDefaults bool) (*Prefs, error) { return p, err } -// Clone returns a deep copy of p. -func (p *Prefs) Clone() *Prefs { - // TODO: write a faster/non-Fatal-y Clone implementation? - p2, err := PrefsFromBytes(p.ToBytes(), false) - if err != nil { - log.Fatalf("Prefs was uncopyable: %v\n", err) - } - return p2 -} - // LoadPrefs loads a legacy relaynode config file into Prefs // with sensible migration defaults set. func LoadPrefs(filename string) (*Prefs, error) { diff --git a/ipn/prefs_clone.go b/ipn/prefs_clone.go new file mode 100644 index 000000000..051c75c4c --- /dev/null +++ b/ipn/prefs_clone.go @@ -0,0 +1,51 @@ +// Copyright (c) 2020 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. + +// Code generated by tailscale.com/cmd/cloner -type Prefs; DO NOT EDIT. + +package ipn + +import ( + "github.com/tailscale/wireguard-go/wgcfg" + "tailscale.com/control/controlclient" + "tailscale.com/wgengine/router" +) + +// Clone makes a deep copy of Prefs. +// The result aliases no memory with the original. +func (src *Prefs) Clone() *Prefs { + if src == nil { + return nil + } + dst := new(Prefs) + *dst = *src + dst.AdvertiseTags = append(src.AdvertiseTags[:0:0], src.AdvertiseTags...) + dst.AdvertiseRoutes = append(src.AdvertiseRoutes[:0:0], src.AdvertiseRoutes...) + if dst.Persist != nil { + dst.Persist = new(controlclient.Persist) + *dst.Persist = *src.Persist + } + return dst +} + +// A compilation failure here means this code must be regenerated, with command: +// tailscale.com/cmd/cloner -type Prefs +var _PrefsNeedsRegeneration = Prefs(struct { + ControlURL string + RouteAll bool + AllowSingleHosts bool + CorpDNS bool + WantRunning bool + ShieldsUp bool + AdvertiseTags []string + Hostname string + OSVersion string + DeviceModel string + NotepadURLs bool + ForceDaemon bool + AdvertiseRoutes []wgcfg.CIDR + NoSNAT bool + NetfilterMode router.NetfilterMode + Persist *controlclient.Persist +}{}) diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 1e56d9e93..3e87c69b7 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -4,7 +4,7 @@ package tailcfg -//go:generate go run tailscale.com/cmd/cloner -type=User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig,RegisterResponse -output=tailcfg_clone.go +//go:generate go run tailscale.com/cmd/cloner --type=User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig,RegisterResponse --clonefunc=true --output=tailcfg_clone.go import ( "bytes"