ipn, ipn/ipnserver: add support for serving in error-message-only mode

So Windows service failures can be propagated to the Windows UI client.
pull/538/head
Brad Fitzpatrick 4 years ago
parent 5280d039c4
commit 3984f9be2f

@ -33,15 +33,19 @@ type Options struct {
// SocketPath, on unix systems, is the unix socket path to listen
// on for frontend connections.
SocketPath string
// Port, on windows, is the localhost TCP port to listen on for
// frontend connections.
Port int
// StatePath is the path to the stored agent state.
StatePath string
// AutostartStateKey, if non-empty, immediately starts the agent
// using the given StateKey. If empty, the agent stays idle and
// waits for a frontend to start it.
AutostartStateKey ipn.StateKey
// LegacyConfigPath optionally specifies the old-style relaynode
// relay.conf location. If both LegacyConfigPath and
// AutostartStateKey are specified and the requested state doesn't
@ -51,6 +55,7 @@ type Options struct {
// TODO(danderson): remove some time after the transition to
// tailscaled is done.
LegacyConfigPath string
// SurviveDisconnects specifies how the server reacts to its
// frontend disconnecting. If true, the server keeps running on
// its existing state, and accepts new frontend connections. If
@ -60,6 +65,10 @@ type Options struct {
// DebugMux, if non-nil, specifies an HTTP ServeMux in which
// to register a debug handler.
DebugMux *http.ServeMux
// ErrorMessage, if not empty, signals that the server will exist
// only to relay the provided critical error message to the user.
ErrorMessage string
}
func pump(logf logger.Logf, ctx context.Context, bs *ipn.BackendServer, s net.Conn) {
@ -79,9 +88,9 @@ func pump(logf logger.Logf, ctx context.Context, bs *ipn.BackendServer, s net.Co
}
}
func Run(rctx context.Context, logf logger.Logf, logid string, opts Options, e wgengine.Engine) (err error) {
runDone := make(chan error, 1)
defer func() { runDone <- err }()
func Run(rctx context.Context, logf logger.Logf, logid string, opts Options, e wgengine.Engine) error {
runDone := make(chan struct{})
defer close(runDone)
listen, _, err := safesocket.Listen(opts.SocketPath, uint16(opts.Port))
if err != nil {
@ -98,6 +107,29 @@ func Run(rctx context.Context, logf logger.Logf, logid string, opts Options, e w
}()
logf("Listening on %v", listen.Addr())
bo := backoff.NewBackoff("ipnserver", logf)
if opts.ErrorMessage != "" {
for i := 1; rctx.Err() == nil; i++ {
s, err := listen.Accept()
if err != nil {
logf("%d: Accept: %v", i, err)
bo.BackOff(rctx, err)
continue
}
serverToClient := func(b []byte) {
ipn.WriteMsg(s, b)
}
go func() {
defer s.Close()
bs := ipn.NewBackendServer(logf, nil, serverToClient)
bs.SendErrorMessage(opts.ErrorMessage)
s.Read(make([]byte, 1))
}()
}
return rctx.Err()
}
var store ipn.StateStore
if opts.StatePath != "" {
store, err = ipn.NewFileStore(opts.StatePath)
@ -164,8 +196,6 @@ func Run(rctx context.Context, logf logger.Logf, logid string, opts Options, e w
}
}
bo := backoff.NewBackoff("ipnserver", logf)
for i := 1; rctx.Err() == nil; i++ {
s, err = listen.Accept()
if err != nil {

@ -80,6 +80,10 @@ func (bs *BackendServer) send(n Notify) {
bs.sendNotifyMsg(b)
}
func (bs *BackendServer) SendErrorMessage(msg string) {
bs.send(Notify{ErrMessage: &msg})
}
// GotCommandMsg parses the incoming message b as a JSON Command and
// calls GotCommand with it.
func (bs *BackendServer) GotCommandMsg(b []byte) error {

Loading…
Cancel
Save