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