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 := ¬ifyServiceHandler{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 }