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