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