github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/keybase_daemon_rpc.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"os"
     9  	"runtime"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/keybase/client/go/libkb"
    14  	"github.com/keybase/client/go/logger"
    15  	"github.com/keybase/client/go/protocol/keybase1"
    16  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    17  	"golang.org/x/net/context"
    18  )
    19  
    20  // KeybaseDaemonRPC implements the KeybaseService interface using RPC
    21  // calls.
    22  type KeybaseDaemonRPC struct {
    23  	*KeybaseServiceBase
    24  	daemonLog logger.Logger
    25  
    26  	// Only used when there's a real connection (i.e., not in
    27  	// testing).
    28  	shutdownFn func()
    29  
    30  	keepAliveCancel context.CancelFunc
    31  
    32  	// protocols (additional to required protocols) to register on server connect
    33  	lock      sync.RWMutex
    34  	protocols []rpc.Protocol
    35  	// server is set in OnConnect. If this is non-nil, subsequent AddProtocol
    36  	// calls triggers a server.Register.
    37  	server *rpc.Server
    38  
    39  	notifyService keybase1.NotifyServiceInterface
    40  }
    41  
    42  var _ keybase1.NotifySessionInterface = (*KeybaseDaemonRPC)(nil)
    43  
    44  var _ keybase1.NotifyKeyfamilyInterface = (*KeybaseDaemonRPC)(nil)
    45  
    46  var _ keybase1.NotifyPaperKeyInterface = (*KeybaseDaemonRPC)(nil)
    47  
    48  var _ keybase1.NotifyFSRequestInterface = (*KeybaseDaemonRPC)(nil)
    49  
    50  var _ keybase1.NotifyTeamInterface = (*KeybaseDaemonRPC)(nil)
    51  
    52  var _ rpc.ConnectionHandler = (*KeybaseDaemonRPC)(nil)
    53  
    54  var _ KeybaseService = (*KeybaseDaemonRPC)(nil)
    55  
    56  var _ keybase1.ImplicitTeamMigrationInterface = (*KeybaseDaemonRPC)(nil)
    57  
    58  func (k *KeybaseDaemonRPC) addKBFSProtocols() {
    59  	// Protocols that KBFS requires
    60  	protocols := []rpc.Protocol{
    61  		keybase1.LogUiProtocol(daemonLogUI{k.daemonLog}),
    62  		keybase1.IdentifyUiProtocol(daemonIdentifyUI{k.daemonLog}),
    63  		keybase1.NotifySessionProtocol(k),
    64  		keybase1.NotifyKeyfamilyProtocol(k),
    65  		keybase1.NotifyPaperKeyProtocol(k),
    66  		keybase1.NotifyFSRequestProtocol(k),
    67  		keybase1.NotifyTeamProtocol(k),
    68  		keybase1.NotifyFavoritesProtocol(k),
    69  		keybase1.TlfKeysProtocol(k),
    70  		keybase1.ReachabilityProtocol(k),
    71  		keybase1.ImplicitTeamMigrationProtocol(k),
    72  	}
    73  
    74  	if k.notifyService != nil {
    75  		k.log.Warning("adding NotifyService protocol")
    76  		protocols = append(protocols, keybase1.NotifyServiceProtocol(k.notifyService))
    77  	}
    78  
    79  	k.AddProtocols(protocols)
    80  }
    81  
    82  // NewKeybaseDaemonRPC makes a new KeybaseDaemonRPC that makes RPC
    83  // calls using the socket of the given Keybase context.
    84  func NewKeybaseDaemonRPC(config Config, kbCtx Context, log logger.Logger,
    85  	debug bool, additionalProtocols []rpc.Protocol) *KeybaseDaemonRPC {
    86  	k := newKeybaseDaemonRPC(config, kbCtx, log)
    87  	k.config = config
    88  	k.daemonLog = logger.New("daemon")
    89  	if debug {
    90  		k.daemonLog.Configure("", true, "")
    91  	}
    92  	conn := NewSharedKeybaseConnection(kbCtx, config, k)
    93  	k.fillClients(conn.GetClient())
    94  	k.shutdownFn = conn.Shutdown
    95  
    96  	if config.Mode().ServiceKeepaliveEnabled() {
    97  		ctx, cancel := context.WithCancel(context.Background())
    98  		k.keepAliveCancel = cancel
    99  		go k.keepAliveLoop(ctx)
   100  	}
   101  	k.notifyService = newNotifyServiceHandler(config, log)
   102  
   103  	k.addKBFSProtocols()
   104  	k.AddProtocols(additionalProtocols)
   105  
   106  	return k
   107  }
   108  
   109  // For testing.
   110  func newKeybaseDaemonRPCWithClient(kbCtx Context, client rpc.GenericClient,
   111  	log logger.Logger) *KeybaseDaemonRPC {
   112  	k := newKeybaseDaemonRPC(nil, kbCtx, log)
   113  	k.fillClients(client)
   114  	// No need for a keepalive loop in this case, since this is only
   115  	// used during testing.
   116  	return k
   117  }
   118  
   119  func newKeybaseDaemonRPC(config Config, kbCtx Context, log logger.Logger) *KeybaseDaemonRPC {
   120  	serviceBase := NewKeybaseServiceBase(config, kbCtx, log)
   121  	if serviceBase == nil {
   122  		return nil
   123  	}
   124  	k := KeybaseDaemonRPC{
   125  		KeybaseServiceBase: serviceBase,
   126  	}
   127  	return &k
   128  }
   129  
   130  func (k *KeybaseDaemonRPC) fillClients(client rpc.GenericClient) {
   131  	k.FillClients(keybase1.IdentifyClient{Cli: client},
   132  		keybase1.UserClient{Cli: client},
   133  		keybase1.TeamsClient{Cli: client},
   134  		keybase1.MerkleClient{Cli: client},
   135  		keybase1.SessionClient{Cli: client},
   136  		keybase1.FavoriteClient{Cli: client},
   137  		keybase1.KbfsClient{Cli: client},
   138  		keybase1.KbfsMountClient{Cli: client},
   139  		keybase1.GitClient{Cli: client},
   140  		keybase1.KvstoreClient{Cli: client},
   141  	)
   142  }
   143  
   144  type daemonLogUI struct {
   145  	log logger.Logger
   146  }
   147  
   148  var _ keybase1.LogUiInterface = daemonLogUI{}
   149  
   150  func (l daemonLogUI) Log(ctx context.Context, arg keybase1.LogArg) error {
   151  	format := "%s"
   152  	// arg.Text.Markup should always be false, so ignore it.
   153  	s := arg.Text.Data
   154  	switch arg.Level {
   155  	case keybase1.LogLevel_DEBUG:
   156  		l.log.CDebugf(ctx, format, s)
   157  	case keybase1.LogLevel_INFO:
   158  		l.log.CInfof(ctx, format, s)
   159  	case keybase1.LogLevel_WARN:
   160  		l.log.CWarningf(ctx, format, s)
   161  	case keybase1.LogLevel_ERROR:
   162  		l.log.CErrorf(ctx, format, s)
   163  	case keybase1.LogLevel_NOTICE:
   164  		l.log.CNoticef(ctx, format, s)
   165  	case keybase1.LogLevel_CRITICAL:
   166  		l.log.CCriticalf(ctx, format, s)
   167  	default:
   168  		l.log.CWarningf(ctx, format, s)
   169  	}
   170  	return nil
   171  }
   172  
   173  type daemonIdentifyUI struct {
   174  	log logger.Logger
   175  }
   176  
   177  var _ keybase1.IdentifyUiInterface = daemonIdentifyUI{}
   178  
   179  func (d daemonIdentifyUI) DelegateIdentifyUI(ctx context.Context) (int, error) {
   180  	d.log.CDebugf(ctx, "DelegateIdentifyUI() (returning 0, UIDelegationUnavailableError{}")
   181  	return 0, libkb.UIDelegationUnavailableError{}
   182  }
   183  
   184  func (d daemonIdentifyUI) Start(ctx context.Context, arg keybase1.StartArg) error {
   185  	d.log.CDebugf(ctx, "Start(%+v)", arg)
   186  	return nil
   187  }
   188  
   189  func (d daemonIdentifyUI) DisplayKey(ctx context.Context, arg keybase1.DisplayKeyArg) error {
   190  	d.log.CDebugf(ctx, "DisplayKey(%+v)", arg)
   191  	return nil
   192  }
   193  
   194  func (d daemonIdentifyUI) ReportLastTrack(ctx context.Context, arg keybase1.ReportLastTrackArg) error {
   195  	d.log.CDebugf(ctx, "ReportLastTrack(%+v)", arg)
   196  	return nil
   197  }
   198  
   199  func (d daemonIdentifyUI) LaunchNetworkChecks(ctx context.Context, arg keybase1.LaunchNetworkChecksArg) error {
   200  	d.log.CDebugf(ctx, "LaunchNetworkChecks(%+v)", arg)
   201  	return nil
   202  }
   203  
   204  func (d daemonIdentifyUI) DisplayTrackStatement(ctx context.Context, arg keybase1.DisplayTrackStatementArg) error {
   205  	d.log.CDebugf(ctx, "DisplayTrackStatement(%+v)", arg)
   206  	return nil
   207  }
   208  
   209  func (d daemonIdentifyUI) FinishWebProofCheck(ctx context.Context, arg keybase1.FinishWebProofCheckArg) error {
   210  	d.log.CDebugf(ctx, "FinishWebProofCheck(%+v)", arg)
   211  	return nil
   212  }
   213  
   214  func (d daemonIdentifyUI) FinishSocialProofCheck(ctx context.Context, arg keybase1.FinishSocialProofCheckArg) error {
   215  	d.log.CDebugf(ctx, "FinishSocialProofCheck(%+v)", arg)
   216  	return nil
   217  }
   218  
   219  func (d daemonIdentifyUI) DisplayCryptocurrency(ctx context.Context, arg keybase1.DisplayCryptocurrencyArg) error {
   220  	d.log.CDebugf(ctx, "DisplayCryptocurrency(%+v)", arg)
   221  	return nil
   222  }
   223  
   224  func (d daemonIdentifyUI) DisplayStellarAccount(ctx context.Context, arg keybase1.DisplayStellarAccountArg) error {
   225  	d.log.CDebugf(ctx, "DisplayStellarAccount(%+v)", arg)
   226  	return nil
   227  }
   228  
   229  func (d daemonIdentifyUI) ReportTrackToken(ctx context.Context, arg keybase1.ReportTrackTokenArg) error {
   230  	d.log.CDebugf(ctx, "ReportTrackToken(%+v)", arg)
   231  	return nil
   232  }
   233  
   234  func (d daemonIdentifyUI) DisplayUserCard(ctx context.Context, arg keybase1.DisplayUserCardArg) error {
   235  	d.log.CDebugf(ctx, "DisplayUserCard(%+v)", arg)
   236  	return nil
   237  }
   238  
   239  func (d daemonIdentifyUI) Confirm(ctx context.Context, arg keybase1.ConfirmArg) (keybase1.ConfirmResult, error) {
   240  	d.log.CDebugf(ctx, "Confirm(%+v) (returning false)", arg)
   241  	return keybase1.ConfirmResult{
   242  		IdentityConfirmed: false,
   243  		RemoteConfirmed:   false,
   244  	}, nil
   245  }
   246  
   247  func (d daemonIdentifyUI) DisplayTLFCreateWithInvite(ctx context.Context,
   248  	arg keybase1.DisplayTLFCreateWithInviteArg) (err error) {
   249  	return nil
   250  }
   251  
   252  func (d daemonIdentifyUI) Dismiss(ctx context.Context,
   253  	_ keybase1.DismissArg) error {
   254  	return nil
   255  }
   256  
   257  func (d daemonIdentifyUI) Cancel(ctx context.Context, sessionID int) error {
   258  	d.log.CDebugf(ctx, "Cancel(%d)", sessionID)
   259  	return nil
   260  }
   261  
   262  func (d daemonIdentifyUI) Finish(ctx context.Context, sessionID int) error {
   263  	d.log.CDebugf(ctx, "Finish(%d)", sessionID)
   264  	return nil
   265  }
   266  
   267  // HandlerName implements the ConnectionHandler interface.
   268  func (*KeybaseDaemonRPC) HandlerName() string {
   269  	return "KeybaseDaemonRPC"
   270  }
   271  
   272  func (k *KeybaseDaemonRPC) registerProtocol(server *rpc.Server, p rpc.Protocol) error {
   273  	k.log.Debug("registering protocol %q", p.Name)
   274  	err := server.Register(p)
   275  	switch err.(type) {
   276  	case nil, rpc.AlreadyRegisteredError:
   277  		return nil
   278  	default:
   279  		k.log.Warning("register protocol %q error", p.Name)
   280  		return err
   281  	}
   282  }
   283  
   284  // AddProtocols adds protocols that are registered on server connect
   285  func (k *KeybaseDaemonRPC) AddProtocols(protocols []rpc.Protocol) {
   286  	if protocols == nil {
   287  		return
   288  	}
   289  
   290  	k.lock.Lock()
   291  	defer k.lock.Unlock()
   292  
   293  	if k.protocols != nil {
   294  		k.protocols = append(k.protocols, protocols...)
   295  	} else {
   296  		k.protocols = protocols
   297  	}
   298  
   299  	// If we are already connected, register these protocols.
   300  	if k.server != nil {
   301  		for _, p := range protocols {
   302  			err := k.registerProtocol(k.server, p)
   303  			if err != nil {
   304  				k.log.Debug("Couldn't register protocol: %+v", err)
   305  			}
   306  		}
   307  	}
   308  }
   309  
   310  // OnConnect implements the ConnectionHandler interface.
   311  func (k *KeybaseDaemonRPC) OnConnect(ctx context.Context,
   312  	conn *rpc.Connection, rawClient rpc.GenericClient,
   313  	server *rpc.Server) (err error) {
   314  	k.lock.Lock()
   315  	defer k.lock.Unlock()
   316  
   317  	for _, p := range k.protocols {
   318  		if err = k.registerProtocol(server, p); err != nil {
   319  			return err
   320  		}
   321  	}
   322  
   323  	// Using conn.GetClient() here would cause problematic
   324  	// recursion.
   325  	c := keybase1.NotifyCtlClient{Cli: rawClient}
   326  	err = c.SetNotifications(ctx, keybase1.NotificationChannels{
   327  		Session:       true,
   328  		Paperkeys:     true,
   329  		Keyfamily:     true,
   330  		Kbfsrequest:   true,
   331  		Reachability:  true,
   332  		Service:       true,
   333  		Team:          true,
   334  		Chatkbfsedits: true,
   335  		Favorites:     true,
   336  	})
   337  	if err != nil {
   338  		return err
   339  	}
   340  
   341  	// Introduce ourselves. TODO: move this to SharedKeybaseConnection
   342  	// somehow?
   343  	configClient := keybase1.ConfigClient{Cli: rawClient}
   344  	err = configClient.HelloIAm(ctx, keybase1.ClientDetails{
   345  		Pid:        os.Getpid(),
   346  		ClientType: k.config.Mode().ClientType(),
   347  		Argv:       os.Args,
   348  		Version:    VersionString(),
   349  	})
   350  	if err != nil {
   351  		return err
   352  	}
   353  
   354  	// Set k.server only if err == nil.
   355  	k.server = server
   356  
   357  	return nil
   358  }
   359  
   360  // OnConnectError implements the ConnectionHandler interface.
   361  func (k *KeybaseDaemonRPC) OnConnectError(err error, wait time.Duration) {
   362  	k.log.Warning("KeybaseDaemonRPC: connection error: %q; retrying in %s",
   363  		err, wait)
   364  }
   365  
   366  // OnDoCommandError implements the ConnectionHandler interface.
   367  func (k *KeybaseDaemonRPC) OnDoCommandError(err error, wait time.Duration) {
   368  	k.log.Warning("KeybaseDaemonRPC: docommand error: %q; retrying in %s",
   369  		err, wait)
   370  }
   371  
   372  // OnDisconnected implements the ConnectionHandler interface.
   373  func (k *KeybaseDaemonRPC) OnDisconnected(ctx context.Context,
   374  	status rpc.DisconnectStatus) {
   375  	if status == rpc.StartingNonFirstConnection {
   376  		k.log.Warning("KeybaseDaemonRPC is disconnected")
   377  	}
   378  
   379  	k.ClearCaches(ctx)
   380  
   381  	k.lock.Lock()
   382  	defer k.lock.Unlock()
   383  	k.server = nil
   384  }
   385  
   386  // ShouldRetry implements the ConnectionHandler interface.
   387  func (k *KeybaseDaemonRPC) ShouldRetry(rpcName string, err error) bool {
   388  	return false
   389  }
   390  
   391  // ShouldRetryOnConnect implements the ConnectionHandler interface.
   392  func (k *KeybaseDaemonRPC) ShouldRetryOnConnect(err error) bool {
   393  	_, inputCanceled := err.(libkb.InputCanceledError)
   394  	return !inputCanceled
   395  }
   396  
   397  func (k *KeybaseDaemonRPC) sendPing(ctx context.Context) {
   398  	ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
   399  	defer cancel()
   400  	err := k.sessionClient.SessionPing(ctx)
   401  	if err != nil {
   402  		k.log.CWarningf(
   403  			ctx, "Background keep alive hit an error: %v", err)
   404  	}
   405  }
   406  
   407  func (k *KeybaseDaemonRPC) keepAliveLoop(ctx context.Context) {
   408  	// If the connection is dropped, we need to re-connect and send
   409  	// another HelloIAm message. However, we can't actually detect
   410  	// when the connection is closed until KBFS makes another outgoing
   411  	// RPC, which might not happen for a while.  So continuously send
   412  	// a cheap RPC in the background, so that OnConnect will get
   413  	// called as soon as the connection comes back.
   414  	ticker := time.NewTicker(1 * time.Second)
   415  	defer ticker.Stop()
   416  	for {
   417  		select {
   418  		case <-ctx.Done():
   419  			return
   420  		case <-ticker.C:
   421  			if k.sessionClient == nil {
   422  				// Clients haven't been filled yet.
   423  				continue
   424  			}
   425  			k.sendPing(ctx)
   426  		}
   427  	}
   428  }
   429  
   430  // Shutdown implements the KeybaseService interface for KeybaseDaemonRPC.
   431  func (k *KeybaseDaemonRPC) Shutdown() {
   432  	if k.shutdownFn != nil {
   433  		k.shutdownFn()
   434  	}
   435  	if k.keepAliveCancel != nil {
   436  		k.keepAliveCancel()
   437  	}
   438  	k.log.Warning("Keybase service shutdown")
   439  
   440  }
   441  
   442  // notifyServiceHandler implements keybase1.NotifyServiceInterface
   443  type notifyServiceHandler struct {
   444  	config Config
   445  	log    logger.Logger
   446  }
   447  
   448  func (s *notifyServiceHandler) Shutdown(_ context.Context, code int) error {
   449  	s.log.Warning("NotifyService: Shutdown")
   450  	if runtime.GOOS == "windows" {
   451  		os.Exit(code)
   452  	}
   453  	return nil
   454  }
   455  
   456  func (s *notifyServiceHandler) HTTPSrvInfoUpdate(_ context.Context, info keybase1.HttpSrvInfo) error {
   457  	return nil
   458  }
   459  
   460  func (s *notifyServiceHandler) HandleKeybaseLink(_ context.Context, _ keybase1.HandleKeybaseLinkArg) error {
   461  	return nil
   462  }
   463  
   464  // newNotifyServiceHandler makes a new NotifyServiceHandler
   465  func newNotifyServiceHandler(config Config, log logger.Logger) keybase1.NotifyServiceInterface {
   466  	s := &notifyServiceHandler{config: config, log: log}
   467  	return s
   468  }
   469  
   470  // FavoritesChanged implements keybase1.NotifyFavoritesClient
   471  func (k *KeybaseDaemonRPC) FavoritesChanged(ctx context.Context,
   472  	uid keybase1.UID) error {
   473  	k.log.Debug("Received FavoritesChanged RPC.")
   474  	k.config.KBFSOps().RefreshCachedFavorites(ctx,
   475  		FavoritesRefreshModeInMainFavoritesLoop)
   476  	return nil
   477  }