mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 13:05:46 +00:00
cmd/tailscaled: graceful shutdown (#534)
Signed-off-by: Dmytro Shynkevych <dmytro@tailscale.com>
This commit is contained in:
parent
6255ce55df
commit
61abab999e
@ -15,8 +15,10 @@
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/apenwarr/fixconsole"
|
"github.com/apenwarr/fixconsole"
|
||||||
@ -27,6 +29,7 @@
|
|||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
"tailscale.com/wgengine/magicsock"
|
"tailscale.com/wgengine/magicsock"
|
||||||
|
"tailscale.com/wgengine/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
// globalStateKey is the ipn.StateKey that tailscaled loads on
|
// globalStateKey is the ipn.StateKey that tailscaled loads on
|
||||||
@ -52,6 +55,7 @@ func main() {
|
|||||||
defaultTunName = "tun"
|
defaultTunName = "tun"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanup := getopt.BoolLong("cleanup", 0, "clean up system state and exit")
|
||||||
fake := getopt.BoolLong("fake", 0, "fake tunnel+routing instead of tuntap")
|
fake := getopt.BoolLong("fake", 0, "fake tunnel+routing instead of tuntap")
|
||||||
debug := getopt.StringLong("debug", 0, "", "Address of debug server")
|
debug := getopt.StringLong("debug", 0, "", "Address of debug server")
|
||||||
tunname := getopt.StringLong("tun", 0, defaultTunName, "tunnel interface name")
|
tunname := getopt.StringLong("tun", 0, defaultTunName, "tunnel interface name")
|
||||||
@ -73,6 +77,11 @@ func main() {
|
|||||||
log.Fatalf("too many non-flag arguments: %#v", getopt.Args()[0])
|
log.Fatalf("too many non-flag arguments: %#v", getopt.Args()[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *cleanup {
|
||||||
|
router.Cleanup(logf, *tunname)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if *statepath == "" {
|
if *statepath == "" {
|
||||||
log.Fatalf("--state is required")
|
log.Fatalf("--state is required")
|
||||||
}
|
}
|
||||||
@ -98,6 +107,20 @@ func main() {
|
|||||||
}
|
}
|
||||||
e = wgengine.NewWatchdog(e)
|
e = wgengine.NewWatchdog(e)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
// Exit gracefully by cancelling the ipnserver context in most common cases:
|
||||||
|
// interrupted from the TTY or killed by a service manager.
|
||||||
|
go func() {
|
||||||
|
interrupt := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
select {
|
||||||
|
case <-interrupt:
|
||||||
|
cancel()
|
||||||
|
case <-ctx.Done():
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
opts := ipnserver.Options{
|
opts := ipnserver.Options{
|
||||||
SocketPath: *socketpath,
|
SocketPath: *socketpath,
|
||||||
Port: 41112,
|
Port: 41112,
|
||||||
@ -107,15 +130,13 @@ func main() {
|
|||||||
SurviveDisconnects: true,
|
SurviveDisconnects: true,
|
||||||
DebugMux: debugMux,
|
DebugMux: debugMux,
|
||||||
}
|
}
|
||||||
err = ipnserver.Run(context.Background(), logf, pol.PublicID.String(), opts, e)
|
err = ipnserver.Run(ctx, logf, pol.PublicID.String(), opts, e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("tailscaled: %v", err)
|
log.Fatalf("tailscaled: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(crawshaw): It would be nice to start a timeout context the moment a signal
|
// Finish uploading logs after closing everything else.
|
||||||
// is received and use that timeout to give us a moment to finish uploading logs
|
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
|
||||||
// here. But the signal is handled inside ipnserver.Run, so some plumbing is needed.
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
cancel()
|
cancel()
|
||||||
pol.Shutdown(ctx)
|
pol.Shutdown(ctx)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ StartLimitBurst=0
|
|||||||
[Service]
|
[Service]
|
||||||
EnvironmentFile=/etc/default/tailscaled
|
EnvironmentFile=/etc/default/tailscaled
|
||||||
ExecStart=/usr/sbin/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/run/tailscale/tailscaled.sock --port $PORT $FLAGS
|
ExecStart=/usr/sbin/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/run/tailscale/tailscaled.sock --port $PORT $FLAGS
|
||||||
|
ExecStopPost=/usr/sbin/tailscaled --cleanup
|
||||||
|
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
|
||||||
|
@ -224,6 +224,7 @@ func Run(rctx context.Context, logf logger.Logf, logid string, opts Options, e w
|
|||||||
}
|
}
|
||||||
stopAll()
|
stopAll()
|
||||||
|
|
||||||
|
b.Shutdown()
|
||||||
return rctx.Err()
|
return rctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ type LocalBackend struct {
|
|||||||
store StateStore
|
store StateStore
|
||||||
backendLogID string
|
backendLogID string
|
||||||
portpoll *portlist.Poller // may be nil
|
portpoll *portlist.Poller // may be nil
|
||||||
|
portpollOnce sync.Once
|
||||||
newDecompressor func() (controlclient.Decompressor, error)
|
newDecompressor func() (controlclient.Decompressor, error)
|
||||||
|
|
||||||
// TODO: these fields are accessed unsafely by concurrent
|
// TODO: these fields are accessed unsafely by concurrent
|
||||||
@ -387,8 +388,10 @@ func (b *LocalBackend) Start(opts Options) error {
|
|||||||
// At this point, we have finished using hostinfo without synchronization,
|
// At this point, we have finished using hostinfo without synchronization,
|
||||||
// so it is safe to start readPoller which concurrently writes to it.
|
// so it is safe to start readPoller which concurrently writes to it.
|
||||||
if b.portpoll != nil {
|
if b.portpoll != nil {
|
||||||
go b.portpoll.Run(b.ctx)
|
b.portpollOnce.Do(func() {
|
||||||
go b.readPoller()
|
go b.portpoll.Run(b.ctx)
|
||||||
|
go b.readPoller()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
|
@ -35,6 +35,13 @@ func New(logf logger.Logf, wgdev *device.Device, tundev tun.Device) (Router, err
|
|||||||
return newUserspaceRouter(logf, wgdev, tundev)
|
return newUserspaceRouter(logf, wgdev, tundev)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cleanup restores the system network configuration to its original state
|
||||||
|
// in case the Tailscale daemon terminated without closing the router.
|
||||||
|
// No other state needs to be instantiated before this runs.
|
||||||
|
func Cleanup(logf logger.Logf, interfaceName string) {
|
||||||
|
// TODO(dmytro): implement this.
|
||||||
|
}
|
||||||
|
|
||||||
// NetfilterMode is the firewall management mode to use when
|
// NetfilterMode is the firewall management mode to use when
|
||||||
// programming the Linux network stack.
|
// programming the Linux network stack.
|
||||||
type NetfilterMode int
|
type NetfilterMode int
|
||||||
|
Loading…
Reference in New Issue
Block a user