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  }