github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/service/main.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package service 5 6 import ( 7 "bufio" 8 "errors" 9 "fmt" 10 "io" 11 "net" 12 "os" 13 "path/filepath" 14 "runtime" 15 "runtime/pprof" 16 "runtime/trace" 17 "sync" 18 "time" 19 20 "golang.org/x/net/context" 21 22 "github.com/keybase/cli" 23 "github.com/keybase/client/go/avatars" 24 "github.com/keybase/client/go/badges" 25 "github.com/keybase/client/go/chat" 26 "github.com/keybase/client/go/chat/attachments" 27 "github.com/keybase/client/go/chat/bots" 28 "github.com/keybase/client/go/chat/commands" 29 "github.com/keybase/client/go/chat/globals" 30 "github.com/keybase/client/go/chat/maps" 31 "github.com/keybase/client/go/chat/search" 32 "github.com/keybase/client/go/chat/storage" 33 "github.com/keybase/client/go/chat/types" 34 "github.com/keybase/client/go/chat/unfurl" 35 "github.com/keybase/client/go/chat/wallet" 36 "github.com/keybase/client/go/contacts" 37 "github.com/keybase/client/go/engine" 38 "github.com/keybase/client/go/ephemeral" 39 "github.com/keybase/client/go/externals" 40 "github.com/keybase/client/go/gregor" 41 "github.com/keybase/client/go/home" 42 "github.com/keybase/client/go/kbhttp/manager" 43 "github.com/keybase/client/go/libcmdline" 44 "github.com/keybase/client/go/libkb" 45 "github.com/keybase/client/go/offline" 46 "github.com/keybase/client/go/protocol/chat1" 47 gregor1 "github.com/keybase/client/go/protocol/gregor1" 48 "github.com/keybase/client/go/protocol/keybase1" 49 "github.com/keybase/client/go/protocol/stellar1" 50 "github.com/keybase/client/go/pvl" 51 "github.com/keybase/client/go/runtimestats" 52 "github.com/keybase/client/go/stellar" 53 "github.com/keybase/client/go/stellar/remote" 54 "github.com/keybase/client/go/stellar/stellargregor" 55 "github.com/keybase/client/go/systemd" 56 "github.com/keybase/client/go/teambot" 57 "github.com/keybase/client/go/teams" 58 "github.com/keybase/client/go/tlfupgrade" 59 "github.com/keybase/go-framed-msgpack-rpc/rpc" 60 ) 61 62 type Service struct { 63 libkb.Contextified 64 globals.ChatContextified 65 66 isDaemon bool 67 chdirTo string 68 lockPid *libkb.LockPIDFile 69 ForkType keybase1.ForkType 70 startCh chan struct{} 71 stopCh chan keybase1.ExitCode 72 logForwarder *logFwd 73 gregor *gregorHandler 74 rekeyMaster *rekeyMaster 75 badger *badges.Badger 76 reachability *reachability 77 home *home.Home 78 tlfUpgrader *tlfupgrade.BackgroundTLFUpdater 79 teamUpgrader *teams.Upgrader 80 walletState *stellar.WalletState 81 offlineRPCCache *offline.RPCCache 82 trackerLoader *TrackerLoader 83 httpSrv *manager.Srv 84 avatarSrv *avatars.Srv 85 referrerListener InstallReferrerListener // Android only 86 87 loginAttemptMu sync.Mutex 88 loginAttempt libkb.LoginAttempt 89 loginSuccess bool 90 oneshotUsername string 91 oneshotPaperkey string 92 } 93 94 type Shutdowner interface { 95 Shutdown() 96 } 97 98 func NewService(g *libkb.GlobalContext, isDaemon bool) *Service { 99 chatG := globals.NewChatContextified(&globals.ChatContext{}) 100 allG := globals.NewContext(g, chatG.ChatG()) 101 return &Service{ 102 Contextified: libkb.NewContextified(g), 103 ChatContextified: chatG, 104 isDaemon: isDaemon, 105 startCh: make(chan struct{}), 106 stopCh: make(chan keybase1.ExitCode), 107 logForwarder: newLogFwd(), 108 rekeyMaster: newRekeyMaster(g), 109 badger: badges.NewBadger(g), 110 gregor: newGregorHandler(allG), 111 home: home.NewHome(g), 112 tlfUpgrader: tlfupgrade.NewBackgroundTLFUpdater(g), 113 trackerLoader: NewTrackerLoader(g), 114 teamUpgrader: teams.NewUpgrader(), 115 walletState: stellar.NewWalletState(g, remote.NewRemoteNet(g)), 116 offlineRPCCache: offline.NewRPCCache(g), 117 httpSrv: manager.NewSrv(g), 118 } 119 } 120 121 func (d *Service) GetStartChannel() <-chan struct{} { 122 return d.startCh 123 } 124 125 func (d *Service) RegisterProtocols(srv *rpc.Server, xp rpc.Transporter, connID libkb.ConnectionID, logReg *logRegister) (err error) { 126 g := d.G() 127 cg := globals.NewContext(g, d.ChatG()) 128 contactsProv := NewCachedContactsProvider(g) 129 130 protocols := []rpc.Protocol{ 131 keybase1.AccountProtocol(NewAccountHandler(xp, g)), 132 keybase1.BTCProtocol(NewCryptocurrencyHandler(xp, g)), 133 keybase1.CryptocurrencyProtocol(NewCryptocurrencyHandler(xp, g)), 134 keybase1.ConfigProtocol(NewConfigHandler(xp, connID, g, d)), 135 keybase1.CryptoProtocol(NewCryptoHandler(g)), 136 keybase1.CtlProtocol(NewCtlHandler(xp, d, g)), 137 keybase1.DelegateUiCtlProtocol(NewDelegateUICtlHandler(xp, connID, g, d.rekeyMaster)), 138 keybase1.DeviceProtocol(NewDeviceHandler(xp, g, d.gregor)), 139 keybase1.FavoriteProtocol(NewFavoriteHandler(xp, g)), 140 keybase1.TlfProtocol(newTlfHandler(xp, cg)), 141 keybase1.IdentifyProtocol(NewIdentifyHandler(xp, g, d)), 142 keybase1.IncomingShareProtocol(NewIncomingShareHandler(xp, g)), 143 keybase1.InstallProtocol(NewInstallHandler(xp, g)), 144 keybase1.KbfsProtocol(NewKBFSHandler(xp, g, d.ChatG(), d)), 145 keybase1.KbfsMountProtocol(NewKBFSMountHandler(xp, g)), 146 keybase1.KvstoreProtocol(NewKVStoreHandler(xp, g)), 147 keybase1.LogProtocol(NewLogHandler(xp, logReg, g)), 148 keybase1.LoginProtocol(NewLoginHandler(xp, g)), 149 keybase1.NotifyCtlProtocol(NewNotifyCtlHandler(xp, connID, g)), 150 keybase1.PGPProtocol(NewPGPHandler(xp, connID, g)), 151 keybase1.PprofProtocol(NewPprofHandler(xp, g)), 152 keybase1.ReachabilityProtocol(newReachabilityHandler(xp, g, d.reachability)), 153 keybase1.RevokeProtocol(NewRevokeHandler(xp, g)), 154 keybase1.ProveProtocol(NewProveHandler(xp, g)), 155 keybase1.SaltpackProtocol(NewSaltpackHandler(xp, g)), 156 keybase1.ScanProofsProtocol(NewScanProofsHandler(xp, g)), 157 keybase1.SecretKeysProtocol(NewSecretKeysHandler(xp, g)), 158 keybase1.SessionProtocol(NewSessionHandler(xp, g)), 159 keybase1.SignupProtocol(NewSignupHandler(xp, g)), 160 keybase1.SigsProtocol(NewSigsHandler(xp, g)), 161 keybase1.TestProtocol(NewTestHandler(xp, g)), 162 keybase1.TrackProtocol(NewTrackHandler(xp, g)), 163 CancelingProtocol(g, keybase1.ApiserverProtocol(NewAPIServerHandler(xp, g)), 164 libkb.RPCCancelerReasonLogout), 165 keybase1.PaperprovisionProtocol(NewPaperProvisionHandler(xp, g)), 166 keybase1.SelfprovisionProtocol(NewSelfProvisionHandler(xp, g)), 167 keybase1.RekeyProtocol(NewRekeyHandler2(xp, g, d.rekeyMaster)), 168 keybase1.NotifyFSRequestProtocol(newNotifyFSRequestHandler(xp, g)), 169 keybase1.GregorProtocol(newGregorRPCHandler(xp, g, d.gregor)), 170 CancelingProtocol(g, chat1.LocalProtocol(newChatLocalHandler(xp, cg, d.gregor)), 171 libkb.RPCCancelerReasonAll), 172 keybase1.SimpleFSProtocol(NewSimpleFSHandler(xp, g)), 173 keybase1.LogsendProtocol(NewLogsendHandler(xp, g)), 174 CancelingProtocol(g, keybase1.TeamsProtocol(NewTeamsHandler(xp, connID, cg, d)), 175 libkb.RPCCancelerReasonLogout), 176 keybase1.TeamSearchProtocol(newTeamSearchHandler(xp, g)), 177 keybase1.BadgerProtocol(newBadgerHandler(xp, g, d.badger)), 178 keybase1.MerkleProtocol(newMerkleHandler(xp, g)), 179 keybase1.GitProtocol(NewGitHandler(xp, g)), 180 keybase1.HomeProtocol(NewHomeHandler(xp, g, d.home)), 181 keybase1.AvatarsProtocol(NewAvatarHandler(xp, g, g.GetAvatarLoader())), 182 keybase1.PhoneNumbersProtocol(NewPhoneNumbersHandler(xp, g)), 183 keybase1.ContactsProtocol(NewContactsHandler(xp, g, contactsProv)), 184 keybase1.EmailsProtocol(NewEmailsHandler(xp, g)), 185 keybase1.InviteFriendsProtocol(NewInviteFriendsHandler(xp, g)), 186 keybase1.Identify3Protocol(newIdentify3Handler(xp, g)), 187 keybase1.AuditProtocol(NewAuditHandler(xp, g)), 188 keybase1.UserSearchProtocol(NewUserSearchHandler(xp, g, contactsProv)), 189 keybase1.BotProtocol(NewBotHandler(xp, g)), 190 keybase1.FeaturedBotProtocol(NewFeaturedBotHandler(xp, g)), 191 keybase1.WotProtocol(NewWebOfTrustHandler(xp, g)), 192 } 193 appStateHandler := newAppStateHandler(xp, g) 194 protocols = append(protocols, keybase1.AppStateProtocol(appStateHandler)) 195 walletHandler := newWalletHandler(xp, g, d.walletState) 196 protocols = append(protocols, CancelingProtocol(g, stellar1.LocalProtocol(walletHandler), 197 libkb.RPCCancelerReasonLogout)) 198 userHandler := NewUserHandler(xp, g, d.ChatG(), d) 199 protocols = append(protocols, keybase1.UserProtocol(userHandler)) 200 protocols = append(protocols, keybase1.DebuggingProtocol(NewDebuggingHandler(xp, g, userHandler, walletHandler))) 201 for _, proto := range protocols { 202 if err = srv.Register(proto); err != nil { 203 return err 204 } 205 } 206 return nil 207 } 208 209 func (d *Service) Handle(c net.Conn) { 210 start := time.Now() 211 xp := rpc.NewTransport(c, libkb.NewRPCLogFactory(d.G()), 212 d.G().LocalNetworkInstrumenterStorage, 213 libkb.MakeWrapError(d.G()), rpc.DefaultMaxFrameLength) 214 server := rpc.NewServer(xp, libkb.MakeWrapError(d.G())) 215 216 cl := make(chan error, 1) 217 connID := d.G().NotifyRouter.AddConnection(xp, cl) 218 219 var logReg *logRegister 220 if d.isDaemon { 221 // Create a new log register object that the Log handler can use to 222 // register a logger. When this function finishes, the logger 223 // will be removed. 224 logReg = newLogRegister(d.logForwarder, d.G().Log) 225 defer logReg.UnregisterLogger() 226 } 227 if err := d.RegisterProtocols(server, xp, connID, logReg); err != nil { 228 d.G().Log.Warning("RegisterProtocols error: %s", err) 229 return 230 } 231 232 // Run the server and wait for it to finish. 233 <-server.Run() 234 // err is always non-nil. 235 err := server.Err() 236 cl <- err 237 if err != io.EOF { 238 d.G().Log.Warning("Run error: %s", err) 239 } 240 241 d.G().Log.Debug("Handle() complete for connection %d [time=%v]", connID, time.Since(start)) 242 } 243 244 func (d *Service) Run() (err error) { 245 mctx := libkb.NewMetaContextBackground(d.G()).WithLogTag("SVC") 246 defer func() { 247 248 d.stopProfile() 249 250 if d.startCh != nil { 251 close(d.startCh) 252 } 253 d.G().NotifyRouter.HandleServiceShutdown() 254 if err != nil { 255 mctx.Info("Service#Run() exiting with error %s (code %d)", err.Error(), d.G().ExitCode) 256 } else { 257 mctx.Debug("Service#Run() clean exit with code %d", d.G().ExitCode) 258 } 259 }() 260 261 mctx.Debug("+ service starting up; forkType=%v", d.ForkType) 262 mctx.PerfDebug("+ service starting up; forkType=%v", d.ForkType) 263 264 d.startProfile() 265 266 // Sets this global context to "service" mode which will toggle a flag 267 // and will also set in motion various go-routine based managers 268 d.G().SetService() 269 uir := NewUIRouter(d.G()) 270 d.G().SetUIRouter(uir) 271 272 // register the service's logForwarder as the external handler for the log module: 273 d.G().Log.SetExternalHandler(d.logForwarder) 274 275 err = d.writeServiceInfo() 276 if err != nil { 277 return 278 } 279 280 if len(d.chdirTo) != 0 { 281 etmp := os.Chdir(d.chdirTo) 282 if etmp != nil { 283 mctx.Warning("Could not change directory to %s: %s", d.chdirTo, etmp) 284 } else { 285 mctx.Info("Changing runtime dir to %s", d.chdirTo) 286 } 287 } 288 289 switch d.G().Env.GetServiceType() { 290 case "": 291 // Not set, do nothing. 292 case "launchd": 293 d.ForkType = keybase1.ForkType_LAUNCHD 294 case "systemd": 295 d.ForkType = keybase1.ForkType_SYSTEMD 296 default: 297 mctx.Warning("Unknown service type: %q", d.G().Env.GetServiceType()) 298 } 299 300 if err = d.GetExclusiveLock(); err != nil { 301 return 302 } 303 if err = d.cleanupSocketFile(); err != nil { 304 return 305 } 306 307 if err = d.G().LocalDb.ForceOpen(); err != nil { 308 return err 309 } 310 if err = d.G().LocalChatDb.ForceOpen(); err != nil { 311 return err 312 } 313 314 var l net.Listener 315 if l, err = d.ConfigRPCServer(); err != nil { 316 return err 317 } 318 319 if err = d.SetupCriticalSubServices(); err != nil { 320 return err 321 } 322 323 if err = d.runOneshot(mctx); err != nil { 324 return err 325 } 326 327 d.SetupChatModules(nil) 328 329 d.RunBackgroundOperations(uir) 330 331 // At this point initialization is complete, and we're about to start the 332 // listen loop. This is the natural point to report "startup successful" to 333 // the supervisor (currently just systemd on Linux). This isn't necessary 334 // for correctness, but it allows commands like "systemctl start keybase.service" 335 // to report startup errors to the terminal, by delaying their return 336 // until they get this notification (Type=notify, in systemd lingo). 337 systemd.NotifyStartupFinished() 338 339 d.G().ExitCode, err = d.ListenLoopWithStopper(l) 340 341 return err 342 } 343 344 func (d *Service) SetupCriticalSubServices() error { 345 allG := globals.NewContext(d.G(), d.ChatG()) 346 mctx := d.MetaContext(context.TODO()) 347 d.G().RuntimeStats = runtimestats.NewRunner(allG) 348 teams.ServiceInit(d.G()) 349 stellar.ServiceInit(d.G(), d.walletState, d.badger) 350 pvl.NewPvlSourceAndInstall(d.G()) 351 avatars.CreateSourceFromEnvAndInstall(d.G()) 352 externals.NewParamProofStoreAndInstall(d.G()) 353 externals.NewExternalURLStoreAndInstall(d.G()) 354 ephemeral.ServiceInit(mctx) 355 teambot.ServiceInit(mctx) 356 d.avatarSrv = avatars.ServiceInit(d.G(), d.httpSrv, d.G().GetAvatarLoader()) 357 contacts.ServiceInit(d.G()) 358 maps.ServiceInit(allG, d.httpSrv) 359 return nil 360 } 361 362 func (d *Service) RunBackgroundOperations(uir *UIRouter) { 363 // These are all background-ish operations that the service performs. 364 // We should revisit these on mobile, or at least, when mobile apps are 365 // backgrounded. 366 d.G().Log.Debug("RunBackgroundOperations: starting") 367 ctx := context.Background() 368 d.tryLogin(ctx, libkb.LoginAttemptOnline) 369 d.chatOutboxPurgeCheck() 370 d.hourlyChecks() 371 d.slowChecks() // 6 hours 372 d.startupGregor() 373 d.startChatModules() 374 d.addGlobalHooks() 375 d.configurePath() 376 d.configureRekey(uir) 377 d.runBackgroundPerUserKeyUpgrade() 378 d.runBackgroundPerUserKeyUpkeep() 379 d.runBackgroundWalletUpkeep() 380 d.runBackgroundBoxAuditRetry() 381 d.runBackgroundBoxAuditScheduler() 382 d.runBackgroundContactSync() 383 d.runBackgroundInviteFriendsPoll() 384 d.runTLFUpgrade() 385 d.runTrackerLoader(ctx) 386 d.runRuntimeStats(ctx) 387 d.runTeamUpgrader(ctx) 388 d.runHomePoller(ctx) 389 d.runMerkleAudit(ctx) 390 d.startInstallReferrerListener(d.MetaContext(ctx)) 391 } 392 393 func (d *Service) purgeOldChatAttachmentData() { 394 purge := func(dir, glob string) { 395 files, err := filepath.Glob(filepath.Join(dir, glob)) 396 if err != nil { 397 d.G().Log.Debug("purgeOldChatAttachmentData: failed to get %s files: %s", glob, err) 398 } else { 399 d.G().Log.Debug("purgeOldChatAttachmentData: %d files to delete for %s", len(files), glob) 400 for _, f := range files { 401 if err := os.Remove(f); err != nil { 402 d.G().Log.Debug("purgeOldChatAttachmentData: failed to remove: name: %s err: %s", f, err) 403 } 404 } 405 } 406 } 407 cacheDir := d.G().GetCacheDir() 408 oldCacheDir := filepath.Dir(cacheDir) 409 for _, dir := range []string{cacheDir, oldCacheDir} { 410 purge(dir, "kbchat*") 411 purge(dir, "avatar*") 412 purge(dir, "prev*") 413 purge(dir, "rncontacts*") 414 } 415 } 416 417 func (d *Service) startChatModules() { 418 kuid := d.G().Env.GetUID() 419 if !kuid.IsNil() { 420 uid := kuid.ToBytes() 421 g := globals.NewContext(d.G(), d.ChatG()) 422 g.PushHandler.Start(context.Background(), uid) 423 g.MessageDeliverer.Start(context.Background(), uid) 424 g.ConvLoader.Start(context.Background(), uid) 425 g.FetchRetrier.Start(context.Background(), uid) 426 g.EphemeralPurger.Start(context.Background(), uid) 427 g.EphemeralTracker.Start(context.Background(), uid) 428 g.InboxSource.Start(context.Background(), uid) 429 g.Indexer.Start(globals.ChatCtx(context.Background(), g, 430 keybase1.TLFIdentifyBehavior_CHAT_SKIP, nil, nil), uid) 431 g.ArchiveRegistry.Start(globals.ChatCtx(context.Background(), g, 432 keybase1.TLFIdentifyBehavior_CHAT_SKIP, nil, nil), uid) 433 g.CoinFlipManager.Start(context.Background(), uid) 434 g.TeamMentionLoader.Start(context.Background(), uid) 435 g.LiveLocationTracker.Start(context.Background(), uid) 436 g.BotCommandManager.Start(context.Background(), uid) 437 g.UIInboxLoader.Start(context.Background(), uid) 438 g.PushShutdownHook(d.stopChatModules) 439 } 440 d.purgeOldChatAttachmentData() 441 } 442 443 func (d *Service) stopChatModules(m libkb.MetaContext) error { 444 <-d.ChatG().PushHandler.Stop(m.Ctx()) 445 <-d.ChatG().MessageDeliverer.Stop(m.Ctx()) 446 <-d.ChatG().ConvLoader.Stop(m.Ctx()) 447 <-d.ChatG().FetchRetrier.Stop(m.Ctx()) 448 <-d.ChatG().EphemeralPurger.Stop(m.Ctx()) 449 <-d.ChatG().EphemeralTracker.Stop(m.Ctx()) 450 <-d.ChatG().InboxSource.Stop(m.Ctx()) 451 <-d.ChatG().Indexer.Stop(m.Ctx()) 452 <-d.ChatG().ArchiveRegistry.Stop(m.Ctx()) 453 <-d.ChatG().CoinFlipManager.Stop(m.Ctx()) 454 <-d.ChatG().TeamMentionLoader.Stop(m.Ctx()) 455 <-d.ChatG().LiveLocationTracker.Stop(m.Ctx()) 456 <-d.ChatG().BotCommandManager.Stop(m.Ctx()) 457 <-d.ChatG().UIInboxLoader.Stop(m.Ctx()) 458 <-d.ChatG().JourneyCardManager.Stop(m.Ctx()) 459 return nil 460 } 461 462 func (d *Service) SetupChatModules(ri func() chat1.RemoteInterface) { 463 g := globals.NewContext(d.G(), d.ChatG()) 464 if ri == nil { 465 ri = d.gregor.GetClient 466 } 467 468 // Add OnLogout/OnDbNuke hooks for any in-memory sources 469 storage.SetupGlobalHooks(g) 470 // Set up main chat data sources 471 boxer := chat.NewBoxer(g) 472 g.CtxFactory = chat.NewCtxFactory(g) 473 g.Badger = d.badger 474 inboxSource := chat.NewInboxSource(g, g.Env.GetInboxSourceType(), ri) 475 g.InboxSource = inboxSource 476 d.badger.SetLocalChatState(inboxSource) 477 478 chatStorage := storage.New(g, nil) 479 g.ConvSource = chat.NewConversationSource(g, g.Env.GetConvSourceType(), 480 boxer, chatStorage, ri) 481 chatStorage.SetAssetDeleter(g.ConvSource) 482 483 g.RegexpSearcher = search.NewRegexpSearcher(g) 484 g.Indexer = search.NewIndexer(g) 485 g.AddDbNukeHook(g.Indexer, "Indexer") 486 g.ArchiveRegistry = chat.NewChatArchiveRegistry(g, ri) 487 g.AddDbNukeHook(g.ArchiveRegistry, "ChatArchiveRegistry") 488 g.ServerCacheVersions = storage.NewServerVersions(g) 489 490 // Syncer and retriers 491 chatSyncer := chat.NewSyncer(g) 492 g.Syncer = chatSyncer 493 g.FetchRetrier = chat.NewFetchRetrier(g) 494 g.ConvLoader = chat.NewBackgroundConvLoader(g) 495 g.EphemeralPurger = chat.NewBackgroundEphemeralPurger(g) 496 g.EphemeralTracker = chat.NewEphemeralTracker(g) 497 g.AddLogoutHook(g.EphemeralTracker, "EphemeralTracker") 498 g.AddDbNukeHook(g.EphemeralTracker, "EphemeralTracker") 499 g.ActivityNotifier = chat.NewNotifyRouterActivityRouter(g) 500 501 // Set up push handler with the badger 502 d.badger.SetInboxVersionSource(storage.NewInboxVersionSource(g)) 503 pushHandler := chat.NewPushHandler(g) 504 g.PushHandler = pushHandler 505 506 // Message sending apparatus 507 s3signer := attachments.NewS3Signer(ri) 508 store := attachments.NewS3Store(g, g.GetRuntimeDir()) 509 attachmentLRUSize := 1000 510 g.AttachmentUploader = attachments.NewUploader(g, store, s3signer, ri, attachmentLRUSize) 511 g.AddDbNukeHook(g.AttachmentUploader, "AttachmentUploader") 512 sender := chat.NewBlockingSender(g, chat.NewBoxer(g), ri) 513 g.MessageDeliverer = chat.NewDeliverer(g, sender, d.gregor) 514 515 // team channel source 516 g.TeamChannelSource = chat.NewTeamChannelSource(g) 517 g.AddLogoutHook(g.TeamChannelSource, "TeamChannelSource") 518 g.AddDbNukeHook(g.TeamChannelSource, "TeamChannelSource") 519 520 if g.Standalone { 521 g.AttachmentURLSrv = types.DummyAttachmentHTTPSrv{} 522 } else { 523 g.AttachmentURLSrv = chat.NewAttachmentHTTPSrv(g, d.httpSrv, 524 chat.NewCachingAttachmentFetcher(g, store, attachmentLRUSize), ri) 525 } 526 g.AddDbNukeHook(g.AttachmentURLSrv, "AttachmentURLSrv") 527 528 g.StellarLoader = stellar.DefaultLoader(g.ExternalG()) 529 g.StellarSender = wallet.NewSender(g) 530 g.StellarPushHandler = g.ExternalG().GetStellar() 531 532 convStorage := chat.NewDevConversationBackedStorage(g, ri) 533 534 g.Unfurler = unfurl.NewUnfurler(g, store, s3signer, convStorage, chat.NewNonblockingSender(g, sender), 535 ri) 536 g.CommandsSource = commands.NewSource(g) 537 g.CoinFlipManager = chat.NewFlipManager(g, ri) 538 g.JourneyCardManager = chat.NewJourneyCardManager(g, ri) 539 g.AddDbNukeHook(g.JourneyCardManager, "JourneyCardManager") 540 g.TeamMentionLoader = chat.NewTeamMentionLoader(g) 541 g.ExternalAPIKeySource = chat.NewRemoteExternalAPIKeySource(g, ri) 542 g.LiveLocationTracker = maps.NewLiveLocationTracker(g) 543 g.BotCommandManager = bots.NewCachingBotCommandManager(g, ri, chat.CreateNameInfoSource) 544 g.UIInboxLoader = chat.NewUIInboxLoader(g) 545 g.UIThreadLoader = chat.NewUIThreadLoader(g, ri) 546 g.ParticipantsSource = chat.NewCachingParticipantSource(g, ri) 547 g.EmojiSource = chat.NewDevConvEmojiSource(g, ri) 548 549 // Set up Offlinables on Syncer 550 chatSyncer.RegisterOfflinable(g.InboxSource) 551 chatSyncer.RegisterOfflinable(g.FetchRetrier) 552 chatSyncer.RegisterOfflinable(g.MessageDeliverer) 553 chatSyncer.RegisterOfflinable(g.UIThreadLoader) 554 555 // Add a tlfHandler into the user changed handler group so we can keep identify info 556 // fresh 557 g.AddUserChangedHandler(chat.NewIdentifyChangedHandler(g)) 558 559 g.ChatHelper = chat.NewHelper(g, ri) 560 } 561 562 func (d *Service) configureRekey(uir *UIRouter) { 563 rkm := d.rekeyMaster 564 rkm.uiRouter = uir 565 d.gregor.PushHandler(rkm) 566 // the rekey master needs to query gregor state, so we have 567 // this unfortunate dependency injection 568 rkm.gregor = d.gregor 569 rkm.Start() 570 } 571 572 func (d *Service) runTLFUpgrade() { 573 d.tlfUpgrader.Run() 574 } 575 576 func (d *Service) runTrackerLoader(ctx context.Context) { 577 d.trackerLoader.Run(ctx) 578 } 579 580 func (d *Service) runRuntimeStats(ctx context.Context) { 581 d.G().RuntimeStats.Start(ctx) 582 } 583 584 func (d *Service) runTeamUpgrader(ctx context.Context) { 585 d.teamUpgrader.Run(libkb.NewMetaContext(ctx, d.G())) 586 } 587 588 func (d *Service) runHomePoller(ctx context.Context) { 589 d.home.RunUpdateLoop(libkb.NewMetaContext(ctx, d.G())) 590 } 591 592 func (d *Service) runMerkleAudit(ctx context.Context) { 593 if libkb.IsMobilePlatform() { 594 d.G().Log.Debug("MerkleAudit disabled (not desktop, not starting)") 595 return 596 } 597 598 eng := engine.NewMerkleAudit(d.G(), &engine.MerkleAuditArgs{}) 599 m := libkb.NewMetaContextBackground(d.G()) 600 if err := engine.RunEngine2(m, eng); err != nil { 601 m.Warning("merkle root background audit error: %v", err) 602 } 603 604 d.G().PushShutdownHook(eng.Shutdown) 605 } 606 607 func (d *Service) startupGregor() { 608 g := d.G() 609 if g.Env.GetGregorDisabled() { 610 g.Log.Debug("Gregor explicitly disabled") 611 } else if !g.Env.GetTorMode().UseSession() { 612 g.Log.Debug("Gregor disabled in Tor mode") 613 } else { 614 g.Log.Debug("connecting to gregord for push notifications") 615 616 // Create gregorHandler instance first so any handlers can connect 617 // to it before we actually connect to gregor (either gregor is down 618 // or we aren't logged in) 619 d.gregor.Init() 620 d.reachability = newReachability(d.G(), d.gregor) 621 d.gregor.setReachability(d.reachability) 622 d.G().ConnectivityMonitor = d.reachability 623 624 d.gregor.badger = d.badger 625 d.G().GregorState = d.gregor 626 d.G().GregorListener = d.gregor 627 628 // Add default handlers 629 userHandler := newUserHandler(d.G()) 630 userHandler.PushUserBlockedHandler(d.home) 631 632 d.gregor.PushHandler(userHandler) 633 // TODO -- get rid of this? 634 d.gregor.PushHandler(newRekeyLogHandler(d.G())) 635 636 d.gregor.PushHandler(newTeamHandler(d.G(), d.badger)) 637 d.gregor.PushHandler(stellargregor.New(d.G(), d.walletState)) 638 d.gregor.PushHandler(d.home) 639 d.gregor.PushHandler(newEKHandler(d.G())) 640 d.gregor.PushHandler(newTeambotHandler(d.G())) 641 d.gregor.PushHandler(newAvatarGregorHandler(d.G(), d.G().GetAvatarLoader())) 642 d.gregor.PushHandler(newPhoneNumbersGregorHandler(d.G())) 643 d.gregor.PushHandler(newEmailsGregorHandler(d.G())) 644 d.gregor.PushHandler(newKBFSFavoritesHandler(d.G())) 645 646 // Connect to gregord 647 if gcErr := d.tryGregordConnect(); gcErr != nil { 648 g.Log.Debug("error connecting to gregord: %s", gcErr) 649 } 650 } 651 } 652 653 func (d *Service) addGlobalHooks() { 654 d.G().AddLoginHook(d) 655 d.G().AddLogoutHook(d, "service/Service") 656 } 657 658 func (d *Service) StartLoopbackServer(loginMode libkb.LoginAttempt) error { 659 660 ctx := context.Background() 661 662 var l net.Listener 663 var err error 664 665 if err = d.GetExclusiveLock(); err != nil { 666 return err 667 } 668 669 if l, err = d.G().MakeLoopbackServer(); err != nil { 670 return err 671 } 672 673 // Make sure we have the same keys in memory in standalone mode as we do in 674 // regular service mode. 675 d.tryLogin(ctx, loginMode) 676 677 go func() { _ = d.ListenLoop(l) }() 678 679 return nil 680 } 681 682 func (d *Service) ensureRuntimeDir() (string, error) { 683 runtimeDir := d.G().Env.GetRuntimeDir() 684 return runtimeDir, os.MkdirAll(runtimeDir, libkb.PermDir) 685 } 686 687 // If the daemon is already running, we need to be able to check what version 688 // it is, in case the client has been updated. 689 func (d *Service) writeServiceInfo() error { 690 _, err := d.ensureRuntimeDir() 691 if err != nil { 692 return err 693 } 694 695 // Write runtime info file 696 rtInfo := libkb.KeybaseServiceInfo(d.G()) 697 return rtInfo.WriteFile(d.G().Env.GetServiceInfoPath(), d.G().Log) 698 } 699 700 func (d *Service) chatOutboxPurgeCheck() { 701 ticker := libkb.NewBgTicker(5 * time.Minute) 702 m := libkb.NewMetaContextBackground(d.G()).WithLogTag("OBOXPRGE") 703 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 704 m.Debug("stopping chatOutboxPurgeCheck loop") 705 ticker.Stop() 706 return nil 707 }) 708 go func() { 709 for { 710 <-ticker.C 711 uid := d.G().Env.GetUID() 712 if uid.IsNil() { 713 continue 714 } 715 gregorUID := gregor1.UID(uid.ToBytes()) 716 g := globals.NewContext(d.G(), d.ChatG()) 717 ephemeralPurged, err := storage.NewOutbox(g, gregorUID).OutboxPurge(context.Background()) 718 if err != nil { 719 m.Debug("OutboxPurge error: %s", err) 720 continue 721 } 722 if len(ephemeralPurged) > 0 { 723 act := chat1.NewChatActivityWithFailedMessage(chat1.FailedMessageInfo{ 724 OutboxRecords: ephemeralPurged, 725 IsEphemeralPurge: true, 726 }) 727 d.ChatG().ActivityNotifier.Activity(context.Background(), gregorUID, chat1.TopicType_NONE, 728 &act, chat1.ChatActivitySource_LOCAL) 729 } 730 } 731 }() 732 } 733 734 func (d *Service) hourlyChecks() { 735 ticker := libkb.NewBgTicker(1 * time.Hour) 736 m := libkb.NewMetaContextBackground(d.G()).WithLogTag("HRLY") 737 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 738 m.Debug("stopping hourlyChecks loop") 739 ticker.Stop() 740 return nil 741 }) 742 go func() { 743 // do this quickly 744 if err := m.LogoutAndDeprovisionIfRevoked(); err != nil { 745 m.Debug("LogoutAndDeprovisionIfRevoked error: %s", err) 746 } 747 for { 748 <-ticker.C 749 m.Debug("+ hourly check loop") 750 m.Debug("| checking if current device revoked") 751 if err := m.LogoutAndDeprovisionIfRevoked(); err != nil { 752 m.Debug("LogoutAndDeprovisionIfRevoked error: %s", err) 753 } 754 755 m.Debug("| checking tracks on an hour timer") 756 err := libkb.CheckTracking(m.G()) 757 if err != nil { 758 m.Debug("CheckTracking error: %s", err) 759 } 760 761 m.Debug("- hourly check loop") 762 } 763 }() 764 } 765 766 func (d *Service) deviceCloneSelfCheck() error { 767 m := libkb.NewMetaContextBackground(d.G()) 768 m = m.WithLogTag("CLONE") 769 before, after, err := libkb.UpdateDeviceCloneState(m) 770 if err != nil { 771 return err 772 } 773 newClones := after - before 774 775 m.Debug("deviceCloneSelfCheck: is there a new clone? %v", newClones > 0) 776 if newClones > 0 { 777 m.Debug("deviceCloneSelfCheck: notifying user %v -> %v restarts", before, after) 778 d.G().NotifyRouter.HandleDeviceCloneNotification(newClones) 779 } 780 return nil 781 } 782 783 func (d *Service) slowChecks() { 784 ticker := libkb.NewBgTicker(6 * time.Hour) 785 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 786 d.G().Log.Debug("stopping slowChecks loop") 787 ticker.Stop() 788 return nil 789 }) 790 go func() { 791 ctx := context.Background() 792 m := libkb.NewMetaContext(ctx, d.G()).WithLogTag("SLOWCHK") 793 // Do this once fast 794 if err := d.deviceCloneSelfCheck(); err != nil { 795 m.Debug("deviceCloneSelfCheck error: %s", err) 796 } 797 for { 798 <-ticker.C 799 m.Debug("+ slow checks loop") 800 m.Debug("| checking if current device should log out") 801 if err := m.LogoutSelfCheck(); err != nil { 802 m.Debug("LogoutSelfCheck error: %s", err) 803 } 804 if m.G().MobileNetState.State().IsLimited() { 805 m.Debug("| skipping clone check, limited net state") 806 continue 807 } 808 m.Debug("| checking if current device is a clone") 809 if err := d.deviceCloneSelfCheck(); err != nil { 810 m.Debug("deviceCloneSelfCheck error: %s", err) 811 } 812 m.Debug("- slow checks loop") 813 } 814 }() 815 } 816 817 func (d *Service) tryGregordConnect() error { 818 loggedIn := d.G().ActiveDevice.Valid() 819 if !loggedIn { 820 // We only respect the loggedIn flag in the no-error case. 821 d.G().Log.Debug("not logged in, so not connecting to gregord") 822 return nil 823 } 824 return d.gregordConnect() 825 } 826 827 func (d *Service) runBackgroundPerUserKeyUpgrade() { 828 eng := engine.NewPerUserKeyUpgradeBackground(d.G(), &engine.PerUserKeyUpgradeBackgroundArgs{}) 829 go func() { 830 m := libkb.NewMetaContextBackground(d.G()) 831 err := engine.RunEngine2(m, eng) 832 if err != nil { 833 m.Warning("per-user-key background upgrade error: %v", err) 834 } 835 }() 836 837 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 838 d.G().Log.Debug("stopping per-user-key background upgrade") 839 eng.Shutdown() 840 return nil 841 }) 842 } 843 844 func (d *Service) runBackgroundPerUserKeyUpkeep() { 845 eng := engine.NewPerUserKeyUpkeepBackground(d.G(), &engine.PerUserKeyUpkeepBackgroundArgs{}) 846 go func() { 847 m := libkb.NewMetaContextBackground(d.G()) 848 err := engine.RunEngine2(m, eng) 849 if err != nil { 850 m.Warning("per-user-key background upkeep error: %v", err) 851 } 852 }() 853 854 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 855 d.G().Log.Debug("stopping per-user-key background upkeep") 856 eng.Shutdown() 857 return nil 858 }) 859 } 860 861 func (d *Service) runBackgroundWalletUpkeep() { 862 eng := engine.NewWalletUpkeepBackground(d.G(), &engine.WalletUpkeepBackgroundArgs{}) 863 go func() { 864 m := libkb.NewMetaContextBackground(d.G()) 865 err := engine.RunEngine2(m, eng) 866 if err != nil { 867 m.Warning("background WalletUpkeep error: %v", err) 868 } 869 }() 870 871 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 872 d.G().Log.Debug("stopping background WalletUpkeep") 873 eng.Shutdown() 874 return nil 875 }) 876 } 877 878 func (d *Service) runBackgroundBoxAuditRetry() { 879 eng := engine.NewBoxAuditRetryBackground(d.G()) 880 go func() { 881 m := libkb.NewMetaContextBackground(d.G()) 882 err := engine.RunEngine2(m, eng) 883 if err != nil { 884 m.Warning("background BoxAuditorRetry error: %v", err) 885 } 886 }() 887 888 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 889 d.G().Log.Debug("stopping background BoxAuditorRetry") 890 eng.Shutdown() 891 return nil 892 }) 893 } 894 895 func (d *Service) runBackgroundBoxAuditScheduler() { 896 eng := engine.NewBoxAuditSchedulerBackground(d.G()) 897 go func() { 898 m := libkb.NewMetaContextBackground(d.G()) 899 err := engine.RunEngine2(m, eng) 900 if err != nil { 901 m.Warning("background BoxAuditorScheduler error: %v", err) 902 } 903 }() 904 905 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 906 d.G().Log.Debug("stopping background BoxAuditorScheduler") 907 eng.Shutdown() 908 return nil 909 }) 910 } 911 912 func (d *Service) runBackgroundContactSync() { 913 eng := engine.NewContactSyncBackground(d.G()) 914 go func() { 915 m := libkb.NewMetaContextBackground(d.G()) 916 err := engine.RunEngine2(m, eng) 917 if err != nil { 918 m.Warning("background ContactSync error: %v", err) 919 } 920 }() 921 922 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 923 d.G().Log.Debug("stopping background ContactSync") 924 eng.Shutdown() 925 return nil 926 }) 927 } 928 929 func (d *Service) runBackgroundInviteFriendsPoll() { 930 eng := engine.NewInviteFriendsPollBackground(d.G()) 931 go func() { 932 m := libkb.NewMetaContextBackground(d.G()) 933 err := engine.RunEngine2(m, eng) 934 if err != nil { 935 m.Warning("background InviteFriendsPoll error: %v", err) 936 } 937 }() 938 939 d.G().PushShutdownHook(func(mctx libkb.MetaContext) error { 940 d.G().Log.Debug("stopping background InviteFriendsPoll") 941 eng.Shutdown() 942 return nil 943 }) 944 } 945 946 func (d *Service) OnLogin(mctx libkb.MetaContext) error { 947 d.rekeyMaster.Login() 948 if err := d.gregordConnect(); err != nil { 949 return err 950 } 951 uid := d.G().Env.GetUID() 952 if !uid.IsNil() { 953 d.startChatModules() 954 d.runTLFUpgrade() 955 d.runTrackerLoader(mctx.Ctx()) 956 } 957 return nil 958 } 959 960 func (d *Service) OnLogout(m libkb.MetaContext) (err error) { 961 defer m.Trace("Service#OnLogout", &err)() 962 defer m.PerfTrace("Service#OnLogout", &err)() 963 log := func(s string) { 964 m.Debug("Service#OnLogout: %s", s) 965 } 966 967 log("canceling live RPCs") 968 d.G().RPCCanceler.CancelLiveContexts(libkb.RPCCancelerReasonLogout) 969 970 log("shutting down chat modules") 971 if err := d.stopChatModules(m); err != nil { 972 log(fmt.Sprintf("unable to stopChatModules %v", err)) 973 } 974 975 log("shutting down gregor") 976 if d.gregor != nil { 977 _ = d.gregor.Reset() 978 } 979 980 log("shutting down rekeyMaster") 981 d.rekeyMaster.Logout() 982 983 log("shutting down badger") 984 if d.badger != nil { 985 d.badger.Clear(context.TODO()) 986 } 987 988 log("shutting down TLF upgrader") 989 if d.tlfUpgrader != nil { 990 _ = d.tlfUpgrader.Shutdown(m) 991 } 992 993 log("resetting wallet state on logout") 994 if d.walletState != nil { 995 d.walletState.Reset(m) 996 } 997 998 log("shutting down tracker loader") 999 if d.trackerLoader != nil { 1000 <-d.trackerLoader.Shutdown(m.Ctx()) 1001 } 1002 1003 return nil 1004 } 1005 1006 func (d *Service) gregordConnect() (err error) { 1007 var uri *rpc.FMPURI 1008 defer d.G().Trace("gregordConnect", &err)() 1009 1010 uri, err = rpc.ParseFMPURI(d.G().Env.GetGregorURI()) 1011 if err != nil { 1012 return err 1013 } 1014 d.G().Log.Debug("| gregor URI: %s", uri) 1015 1016 // If we are already connected, then shutdown and reset the gregor 1017 // handler 1018 if d.gregor.IsConnected() { 1019 if err := d.gregor.Reset(); err != nil { 1020 return err 1021 } 1022 } 1023 1024 // Connect to gregord 1025 return d.gregor.Connect(uri) 1026 } 1027 1028 // ReleaseLock releases the locking pidfile by closing, unlocking and 1029 // deleting it. 1030 func (d *Service) ReleaseLock(mctx libkb.MetaContext) error { 1031 d.G().Log.Debug("Releasing lock file") 1032 return d.lockPid.Close() 1033 } 1034 1035 // GetExclusiveLockWithoutAutoUnlock grabs the exclusive lock over running 1036 // keybase and continues to hold the lock. The caller is then required to 1037 // manually release this lock via ReleaseLock() 1038 func (d *Service) GetExclusiveLockWithoutAutoUnlock() error { 1039 if _, err := d.ensureRuntimeDir(); err != nil { 1040 return err 1041 } 1042 return d.lockPIDFile() 1043 } 1044 1045 // GetExclusiveLock grabs the exclusive lock over running keybase 1046 // and then installs a shutdown hook to release the lock automatically 1047 // on shutdown. 1048 func (d *Service) GetExclusiveLock() error { 1049 if err := d.GetExclusiveLockWithoutAutoUnlock(); err != nil { 1050 return err 1051 } 1052 d.G().PushShutdownHook(d.ReleaseLock) 1053 return nil 1054 } 1055 1056 func (d *Service) cleanupSocketFile() error { 1057 // Short circuit if we're running under socket activation -- the socket 1058 // file is already set up for us, and we mustn't delete it. 1059 if systemd.IsSocketActivated() { 1060 return nil 1061 } 1062 sf, err := d.G().Env.GetSocketBindFile() 1063 if err != nil { 1064 return err 1065 } 1066 if exists, err := libkb.FileExists(sf); err != nil { 1067 return err 1068 } else if exists { 1069 d.G().Log.Debug("removing stale socket file: %s", sf) 1070 if err = os.Remove(sf); err != nil { 1071 d.G().Log.Warning("error removing stale socket file: %s", err) 1072 return err 1073 } 1074 } 1075 return nil 1076 } 1077 1078 func (d *Service) lockPIDFile() (err error) { 1079 var fn string 1080 if fn, err = d.G().Env.GetPidFile(); err != nil { 1081 return err 1082 } 1083 if err = libkb.MakeParentDirs(d.G().Log, fn); err != nil { 1084 return err 1085 } 1086 d.lockPid = libkb.NewLockPIDFile(d.G(), fn) 1087 if err = d.lockPid.Lock(); err != nil { 1088 return err 1089 } 1090 d.G().Log.Debug("Lock pidfile: %s\n", fn) 1091 return nil 1092 } 1093 1094 func (d *Service) ConfigRPCServer() (net.Listener, error) { 1095 // First, check to see if we've been launched with socket activation by 1096 // systemd. If so, the socket is already open. Otherwise open it ourselves. 1097 // NOTE: We no longer configure our keybse.service and kbfs.service units 1098 // to be socket-activated by default. It was causing too much trouble when 1099 // non-systemd instances deleted the socket files. It's possible this issue 1100 // will get fixed in future versions of systemd; see 1101 // https://github.com/systemd/systemd/issues/7274. 1102 listener, err := systemd.GetListenerFromEnvironment() 1103 if err != nil { 1104 d.G().Log.Error("unexpected error in GetListenerFromEnvironment: %#v", err) 1105 return nil, err 1106 } else if listener != nil { 1107 d.G().Log.Debug("Systemd socket activation in use. Accepting connections on fd 3.") 1108 } else { 1109 d.G().Log.Debug("No socket activation in the environment. Binding to a new socket.") 1110 listener, err = d.G().BindToSocket() 1111 if err != nil { 1112 return nil, err 1113 } 1114 } 1115 1116 if d.startCh != nil { 1117 close(d.startCh) 1118 d.startCh = nil 1119 } 1120 return listener, nil 1121 } 1122 1123 func (d *Service) Stop(exitCode keybase1.ExitCode) { 1124 d.G().Log.Debug("Beginning the process of stopping the service") 1125 d.stopCh <- exitCode 1126 } 1127 1128 func (d *Service) ListenLoopWithStopper(l net.Listener) (exitCode keybase1.ExitCode, err error) { 1129 ch := make(chan error) 1130 go func() { 1131 ch <- d.ListenLoop(l) 1132 }() 1133 exitCode = <-d.stopCh 1134 l.Close() 1135 d.G().Log.Debug("Left listen loop w/ exit code %d\n", exitCode) 1136 return exitCode, <-ch 1137 } 1138 1139 func (d *Service) ListenLoop(l net.Listener) (err error) { 1140 d.G().Log.Debug("+ Enter ListenLoop()") 1141 for { 1142 var c net.Conn 1143 if c, err = l.Accept(); err != nil { 1144 1145 if libkb.IsSocketClosedError(err) { 1146 d.G().Log.Debug("ListenLoop socket/pipe is closed") 1147 err = nil 1148 } 1149 1150 d.G().Log.Debug("+ Leaving ListenLoop() w/ error %v", err) 1151 return 1152 } 1153 go d.Handle(c) 1154 } 1155 } 1156 1157 func (d *Service) runOneshot(mctx libkb.MetaContext) (err error) { 1158 if len(d.oneshotUsername) == 0 { 1159 return nil 1160 } 1161 mctx.Debug("Oneshot login with username: %s", d.oneshotUsername) 1162 pk, err := d.getPaperKey(mctx) 1163 if err != nil { 1164 return err 1165 } 1166 eng := engine.NewLoginOneshot(mctx.G(), keybase1.LoginOneshotArg{ 1167 Username: d.oneshotUsername, 1168 PaperKey: pk, 1169 }) 1170 return engine.RunEngine2(mctx, eng) 1171 } 1172 1173 func (d *Service) getPaperKey(mctx libkb.MetaContext) (key string, err error) { 1174 if len(d.oneshotPaperkey) > 0 { 1175 return d.oneshotPaperkey, nil 1176 } 1177 envVar := "KEYBASE_PAPERKEY" 1178 key = os.Getenv(envVar) 1179 if len(key) > 0 { 1180 return key, nil 1181 } 1182 mctx.Info("Reading paperkey from standard input in oneshot mode") 1183 1184 key, err = bufio.NewReader(os.Stdin).ReadString('\n') 1185 if err == io.EOF && len(key) > 0 { 1186 err = nil 1187 } 1188 if len(key) < 5 { 1189 return "", fmt.Errorf("bad paper key read from standard input in oneshot mode") 1190 } 1191 return key, err 1192 } 1193 1194 func (d *Service) ParseArgv(ctx *cli.Context) error { 1195 d.chdirTo = ctx.String("chdir") 1196 if ctx.Bool("auto-forked") { 1197 d.ForkType = keybase1.ForkType_AUTO 1198 } else if ctx.Bool("watchdog-forked") { 1199 d.ForkType = keybase1.ForkType_WATCHDOG 1200 } else if ctx.Bool("launchd-forked") { 1201 d.ForkType = keybase1.ForkType_LAUNCHD 1202 } 1203 d.oneshotUsername = ctx.String("oneshot-username") 1204 d.oneshotPaperkey = ctx.String("oneshot-paperkey") 1205 if len(d.oneshotPaperkey) > 0 && len(d.oneshotUsername) == 0 { 1206 return fmt.Errorf("Cannot use --oneshot-paperkey without the --oneshot-username option") 1207 } 1208 return nil 1209 } 1210 1211 func NewCmdService(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command { 1212 return cli.Command{ 1213 Name: "service", 1214 Usage: "start the Keybase service to power all other CLI options", 1215 Flags: []cli.Flag{ 1216 cli.StringFlag{ 1217 Name: "chdir", 1218 Usage: "Specify where to run as a daemon (via chdir)", 1219 }, 1220 cli.StringFlag{ 1221 Name: "label", 1222 Usage: "Specifying a label can help identify services.", 1223 }, 1224 cli.BoolFlag{ 1225 Name: "auto-forked", 1226 Usage: "Specify if this binary was auto-forked from the client", 1227 }, 1228 cli.BoolFlag{ 1229 Name: "watchdog-forked", 1230 Usage: "Specify if this binary was started by the watchdog", 1231 }, 1232 cli.StringFlag{ 1233 Name: "u, oneshot-username", 1234 Usage: "In oneshot mode, startup with username", 1235 }, 1236 cli.StringFlag{ 1237 Name: "p, oneshot-paperkey", 1238 Usage: "In oneshot mode, startup with paperkey; DANGEROUS to pass paperkey as a parameter", 1239 }, 1240 }, 1241 Description: `"keybase service" starts up the "service" process that powers all of Keybase. 1242 Usually it runs in the background as part of your OS's packaging of the Keybase, but it 1243 can also run as a foreground process (useful in development or for bots). 1244 1245 There is special support for running the service and immediately logging in via "oneshot 1246 mode", which is particularly userful for bots. Specify --onershot-username and a paper key 1247 to enable this option. We recommend passing the paper key via standard input, or 1248 the KEBASE_PAPERKEY= environment variable, but this subcommand will dangerously accept 1249 the paper key via command line option.`, 1250 Action: func(c *cli.Context) { 1251 cl.ChooseCommand(NewService(g, true /* isDaemon */), "service", c) 1252 cl.SetService() 1253 }, 1254 } 1255 } 1256 1257 func (d *Service) GetUsage() libkb.Usage { 1258 return libkb.ServiceUsage 1259 } 1260 1261 func GetCommands(cl *libcmdline.CommandLine, g *libkb.GlobalContext) []cli.Command { 1262 return []cli.Command{ 1263 NewCmdService(cl, g), 1264 } 1265 } 1266 1267 func (d *Service) GregorDismiss(id gregor.MsgID) error { 1268 if d.gregor == nil { 1269 return errors.New("can't gregor dismiss without a gregor") 1270 } 1271 return d.gregor.DismissItem(context.Background(), gregor1.IncomingClient{Cli: d.gregor.cli}, id) 1272 } 1273 1274 func (d *Service) GregorInject(cat string, body []byte) (gregor.MsgID, error) { 1275 if d.gregor == nil { 1276 return nil, errors.New("can't gregor inject without a gregor") 1277 } 1278 return d.gregor.InjectItem(context.TODO(), cat, body, gregor1.TimeOrOffset{}) 1279 } 1280 1281 func (d *Service) GregorInjectOutOfBandMessage(sys string, body []byte) error { 1282 if d.gregor == nil { 1283 return errors.New("can't gregor inject without a gregor") 1284 } 1285 return d.gregor.InjectOutOfBandMessage(context.TODO(), sys, body) 1286 } 1287 1288 func (d *Service) HasGregor() bool { 1289 return d.gregor != nil && d.gregor.IsConnected() 1290 } 1291 1292 func (d *Service) SimulateGregorCrashForTesting() { 1293 if d.HasGregor() { 1294 d.gregor.simulateCrashForTesting() 1295 } else { 1296 d.G().Log.Warning("Can't simulate a gregor crash without a gregor") 1297 } 1298 } 1299 1300 func (d *Service) SetGregorPushStateFilter(f func(m gregor.Message) bool) { 1301 d.gregor.SetPushStateFilter(f) 1302 } 1303 1304 func (d *Service) GetUserAvatar(username string) (string, error) { 1305 if d.avatarSrv == nil { 1306 return "", fmt.Errorf("AvatarSrv is not ready") 1307 } 1308 1309 return d.avatarSrv.GetUserAvatar(username) 1310 } 1311 1312 // configurePath is a somewhat unfortunate hack, but as it currently stands, 1313 // when the keybase service is run out of launchd, its path is minimal and 1314 // often can't find the GPG location. We have hacks around this for CLI operation, 1315 // in which the CLI forwards its path to the service, and the service enlarges 1316 // its path accordingly. In this way, the service can get access to path additions 1317 // inserted by the user's shell startup scripts. However the same mechanism doesn't 1318 // apply to a GUI-driven workload, since the Electron GUI, like the Go service, is 1319 // launched from launchd and therefore has the wrong path. This function currently 1320 // noops on all platforms aside from macOS, but we can expand it later as needs be. 1321 func (d *Service) configurePath() { 1322 defer d.G().Trace("Service#configurePath", nil)() 1323 1324 var newDirs string 1325 switch runtime.GOOS { 1326 case "darwin", "ios": 1327 newDirs = "/usr/local/bin:/usr/local/MacGPG2/bin" 1328 default: 1329 } 1330 if newDirs != "" { 1331 err := mergeIntoPath(d.G(), newDirs) 1332 if err != nil { 1333 d.G().Log.Debug("Error merging into path: %+v", err) 1334 } 1335 } 1336 } 1337 1338 // tryLogin runs LoginOffline which will load the local session file and unlock the 1339 // local device keys without making any network requests. 1340 // 1341 // If that fails for any reason, LoginProvisionedDevice is used, which should get 1342 // around any issue where the session.json file is out of date or missing since the 1343 // last time the service started. 1344 func (d *Service) tryLogin(ctx context.Context, mode libkb.LoginAttempt) { 1345 d.loginAttemptMu.Lock() 1346 defer d.loginAttemptMu.Unlock() 1347 1348 m := libkb.NewMetaContext(ctx, d.G()) 1349 1350 if d.loginAttempt == libkb.LoginAttemptOnline { 1351 m.Debug("login online attempt already tried, nothing to do") 1352 return 1353 } 1354 if d.loginSuccess { 1355 m.Debug("already logged in successfully, so nothing left to do") 1356 return 1357 } 1358 1359 if mode == libkb.LoginAttemptOffline && d.loginAttempt == libkb.LoginAttemptOffline { 1360 m.Debug("already tried a login attempt offline") 1361 return 1362 } 1363 1364 if mode == libkb.LoginAttemptNone { 1365 m.Debug("no login attempt made due to loginAttemptNone flag passed") 1366 return 1367 } 1368 1369 d.loginAttempt = mode 1370 eng := engine.NewLoginOffline(d.G()) 1371 err := engine.RunEngine2(m, eng) 1372 if err == nil { 1373 m.Debug("login offline success") 1374 d.loginSuccess = true 1375 return 1376 } 1377 1378 if mode == libkb.LoginAttemptOffline { 1379 m.Debug("not continuing with online login") 1380 return 1381 } 1382 1383 m.Debug("error running LoginOffline on service startup: %s", err) 1384 m.Debug("trying LoginProvisionedDevice") 1385 1386 // Standalone mode quirk here. We call tryLogin when client is 1387 // launched in standalone to unlock the same keys that we would 1388 // have in service mode. But NewLoginProvisionedDevice engine 1389 // needs KbKeyrings and not every command sets it up. Ensure 1390 // Keyring is available. 1391 1392 // TODO: We will be phasing out KbKeyrings usage flag, or even 1393 // usage flags entirely. Then this will not be needed because 1394 // Keyrings will always be loaded. 1395 1396 if m.G().Keyrings == nil { 1397 m.Debug("tryLogin: Configuring Keyrings") 1398 err := m.G().ConfigureKeyring() 1399 if err != nil { 1400 m.Debug("error configuring keyring: %s", err) 1401 } 1402 } 1403 1404 deng := engine.NewLoginProvisionedDevice(d.G(), "") 1405 deng.SecretStoreOnly = true 1406 if err := engine.RunEngine2(m, deng); err != nil { 1407 m.Debug("error running LoginProvisionedDevice on service startup: %s", err) 1408 } else { 1409 m.Debug("success running LoginOffline on service startup") 1410 d.loginSuccess = true 1411 } 1412 } 1413 1414 func (d *Service) startProfile() { 1415 cpu := os.Getenv("KEYBASE_CPUPROFILE") 1416 if cpu != "" { 1417 f, err := os.Create(cpu) 1418 if err != nil { 1419 d.G().Log.Warning("error creating cpu profile: %s", err) 1420 } else { 1421 d.G().Log.Debug("+ starting service cpu profile in %s", cpu) 1422 err := pprof.StartCPUProfile(f) 1423 if err != nil { 1424 d.G().Log.Warning("error starting CPU profile: %s", err) 1425 } 1426 } 1427 } 1428 1429 tr := os.Getenv("KEYBASE_SVCTRACE") 1430 if tr != "" { 1431 f, err := os.Create(tr) 1432 if err != nil { 1433 d.G().Log.Warning("error creating service trace: %s", err) 1434 } else { 1435 d.G().Log.Debug("+ starting service trace: %s", tr) 1436 err := trace.Start(f) 1437 if err != nil { 1438 d.G().Log.Warning("error starting trace: %s", err) 1439 } 1440 } 1441 } 1442 } 1443 1444 func (d *Service) stopProfile() { 1445 if os.Getenv("KEYBASE_CPUPROFILE") != "" { 1446 d.G().Log.Debug("stopping cpu profile") 1447 pprof.StopCPUProfile() 1448 } 1449 1450 if os.Getenv("KEYBASE_SVCTRACE") != "" { 1451 d.G().Log.Debug("stopping service execution trace") 1452 trace.Stop() 1453 } 1454 1455 mem := os.Getenv("KEYBASE_MEMPROFILE") 1456 if mem == "" { 1457 return 1458 } 1459 f, err := os.Create(mem) 1460 if err != nil { 1461 d.G().Log.Warning("could not create memory profile: %s", err) 1462 return 1463 } 1464 defer f.Close() 1465 1466 runtime.GC() // get up-to-date statistics 1467 if err := pprof.WriteHeapProfile(f); err != nil { 1468 d.G().Log.Warning("could not write memory profile: %s", err) 1469 } 1470 d.G().Log.Debug("wrote memory profile %s", mem) 1471 1472 var mems runtime.MemStats 1473 runtime.ReadMemStats(&mems) 1474 d.G().Log.Debug("runtime mem alloc: %v", mems.Alloc) 1475 d.G().Log.Debug("runtime total alloc: %v", mems.TotalAlloc) 1476 d.G().Log.Debug("runtime heap alloc: %v", mems.HeapAlloc) 1477 d.G().Log.Debug("runtime heap sys: %v", mems.HeapSys) 1478 } 1479 1480 func (d *Service) StartStandaloneChat(g *libkb.GlobalContext) error { 1481 g.ConnectionManager = libkb.NewConnectionManager() 1482 g.NotifyRouter = libkb.NewNotifyRouter(g) 1483 1484 d.SetupChatModules(nil) 1485 d.startupGregor() 1486 d.startChatModules() 1487 1488 return nil 1489 } 1490 1491 func assertLoggedIn(ctx context.Context, g *libkb.GlobalContext) error { 1492 loggedIn := g.ActiveDevice.Valid() 1493 if !loggedIn { 1494 return libkb.LoginRequiredError{} 1495 } 1496 return nil 1497 }