github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/service/main.go (about)

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