tailscale/control/controlclient/client.go
Maisem Ali d06a75dcd0 ipn/ipnlocal: fix deadlock in resetControlClientLocked
resetControlClientLocked is called while b.mu was held and
would call cc.Shutdown which would wait for the observer queue
to drain.
However, there may be active callbacks from cc already waiting for
b.mu resulting in a deadlock.

This makes it so that resetControlClientLocked does not call
Shutdown, and instead just returns the value.
It also makes it so that any status received from previous cc
are ignored.

Updates tailscale/corp#12827

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2023-09-02 13:47:32 -07:00

82 lines
3.2 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package controlclient implements the client for the Tailscale
// control plane.
//
// It handles authentication, port picking, and collects the local
// network configuration.
package controlclient
import (
"context"
"tailscale.com/tailcfg"
)
type LoginFlags int
const (
LoginDefault = LoginFlags(0)
LoginInteractive = LoginFlags(1 << iota) // force user login and key refresh
LoginEphemeral // set RegisterRequest.Ephemeral
)
// Client represents a client connection to the control server.
// Currently this is done through a pair of polling https requests in
// the Auto client, but that might change eventually.
//
// The Client must be comparable as it is used by the Observer to detect stale
// clients.
type Client interface {
// Shutdown closes this session, which should not be used any further
// afterwards.
Shutdown()
// Login begins an interactive or non-interactive login process.
// Client will eventually call the Status callback with either a
// LoginFinished flag (on success) or an auth URL (if further
// interaction is needed).
Login(*tailcfg.Oauth2Token, LoginFlags)
// Logout starts a synchronous logout process. It doesn't return
// until the logout operation has been completed.
Logout(context.Context) error
// SetPaused pauses or unpauses the controlclient activity as much
// as possible, without losing its internal state, to minimize
// unnecessary network activity.
// TODO: It might be better to simply shutdown the controlclient and
// make a new one when it's time to unpause.
SetPaused(bool)
// AuthCantContinue returns whether authentication is blocked. If it
// is, you either need to visit the auth URL (previously sent in a
// Status callback) or call the Login function appropriately.
// TODO: this probably belongs in the Status itself instead.
AuthCantContinue() bool
// SetHostinfo changes the Hostinfo structure that will be sent in
// subsequent node registration requests.
// TODO: a server-side change would let us simply upload this
// in a separate http request. It has nothing to do with the rest of
// the state machine.
SetHostinfo(*tailcfg.Hostinfo)
// SetNetinfo changes the NetIinfo structure that will be sent in
// subsequent node registration requests.
// TODO: a server-side change would let us simply upload this
// in a separate http request. It has nothing to do with the rest of
// the state machine.
SetNetInfo(*tailcfg.NetInfo)
// SetTKAHead changes the TKA head hash value that will be sent in
// subsequent netmap requests.
SetTKAHead(headHash string)
// UpdateEndpoints changes the Endpoint structure that will be sent
// in subsequent node registration requests.
// TODO: a server-side change would let us simply upload this
// in a separate http request. It has nothing to do with the rest of
// the state machine.
UpdateEndpoints(endpoints []tailcfg.Endpoint)
}
// UserVisibleError is an error that should be shown to users.
type UserVisibleError string
func (e UserVisibleError) Error() string { return string(e) }
func (e UserVisibleError) UserVisibleError() string { return string(e) }