1/// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4//
5// globals
6//
7//   All of the global objects in the libkb namespace that are shared
8//   and mutated across various source files are here.  They are
9//   accessed like `G.Log` or `G.Env`.  They're kept
10//   under the `G` namespace to better keep track of them all.
11//
12//   The globals are built up gradually as the process comes up.
13//   At first, we only have a logger, but eventually we add
14//   command-line flags, configuration and environment, and accordingly,
15//   might actually go back and change the Logger.
16
17package libkb
18
19import (
20	"errors"
21	"fmt"
22	"io"
23	"log"
24	"os"
25	"runtime"
26	"sync"
27	"time"
28
29	logger "github.com/keybase/client/go/logger"
30	keybase1 "github.com/keybase/client/go/protocol/keybase1"
31	clockwork "github.com/keybase/clockwork"
32	context "golang.org/x/net/context"
33)
34
35var IsIPad bool // Set by bind's Init.
36
37type ShutdownHook func(mctx MetaContext) error
38
39type LoginHook interface {
40	OnLogin(mctx MetaContext) error
41}
42
43type LogoutHook interface {
44	OnLogout(mctx MetaContext) error
45}
46
47type DbNukeHook interface {
48	OnDbNuke(mctx MetaContext) error
49}
50
51type GlobalContext struct {
52	Log                              logger.Logger         // Handles all logging
53	PerfLog                          logger.Logger         // Handles all performance event logging
54	VDL                              *VDebugLog            // verbose debug log
55	GUILogFile                       *logger.LogFileWriter // GUI logs
56	Env                              *Env                  // Env variables, cmdline args & config
57	SKBKeyringMu                     *sync.Mutex           // Protects all attempts to mutate the SKBKeyringFile
58	Keyrings                         *Keyrings             // Gpg Keychains holding keys
59	perUserKeyringMu                 *sync.Mutex
60	perUserKeyring                   *PerUserKeyring             // Keyring holding per user keys
61	API                              API                         // How to make a REST call to the server
62	Resolver                         Resolver                    // cache of resolve results
63	LocalNetworkInstrumenterStorage  *DiskInstrumentationStorage // Instrument Local RPC calls
64	RemoteNetworkInstrumenterStorage *DiskInstrumentationStorage // Instrument Remote API/RPC calls
65	LocalDb                          *JSONLocalDb                // Local DB for cache
66	LocalChatDb                      *JSONLocalDb                // Local DB for cache
67	MerkleClient                     MerkleClientInterface       // client for querying server's merkle sig tree
68	XAPI                             ExternalAPI                 // for contacting Twitter, Github, etc.
69	DNSNSFetcher                     DNSNameServerFetcher        // The mobile apps potentially pass an implementor of this interface which is used to grab currently configured DNS name servers
70	MobileNetState                   *MobileNetState             // The kind of network connection for the currently running instance of the app
71	MobileAppState                   *MobileAppState             // The state of focus for the currently running instance of the app
72	DesktopAppState                  *DesktopAppState            // The state of focus for the currently running instance of the app
73	ChatHelper                       ChatHelper                  // conveniently send chat messages
74	RPCCanceler                      *RPCCanceler                // register live RPCs so they can be cancelleed en masse
75	IdentifyDispatch                 *IdentifyDispatch           // get notified of identify successes
76	Identify3State                   *Identify3State             // keep track of Identify3 sessions
77	vidMu                            *sync.Mutex                 // protect VID
78	RuntimeStats                     RuntimeStats                // performance runtime stats
79
80	cacheMu                *sync.RWMutex   // protects all caches
81	ProofCache             *ProofCache     // where to cache proof results
82	trackCache             *TrackCache     // cache of IdentifyOutcomes for tracking purposes
83	identify2Cache         Identify2Cacher // cache of Identify2 results for fast-pathing identify2 RPCS
84	linkCache              *LinkCache      // cache of ChainLinks
85	upakLoader             UPAKLoader      // Load flat users with the ability to hit the cache
86	teamLoader             TeamLoader      // Play back teams for id/name properties
87	fastTeamLoader         FastTeamLoader  // Play back team in "fast" mode for keys and names only
88	hiddenTeamChainManager HiddenTeamChainManager
89	TeamRoleMapManager     TeamRoleMapManager
90	IDLocktab              *LockTable
91	loadUserLockTab        *LockTable
92	teamAuditor            TeamAuditor
93	teamBoxAuditor         TeamBoxAuditor
94	stellar                Stellar            // Stellar related ops
95	deviceEKStorage        DeviceEKStorage    // Store device ephemeral keys
96	userEKBoxStorage       UserEKBoxStorage   // Store user ephemeral key boxes
97	teamEKBoxStorage       TeamEKBoxStorage   // Store team ephemeral key boxes
98	teambotEKBoxStorage    TeamEKBoxStorage   // Store team bot ephemeral key boxes
99	ekLib                  EKLib              // Wrapper to call ephemeral key methods
100	teambotBotKeyer        TeambotBotKeyer    // TeambotKeyer for bot members
101	teambotMemberKeyer     TeambotMemberKeyer // TeambotKeyer for non-bot members
102	itciCacher             LRUer              // Cacher for implicit team conflict info
103	iteamCacher            MemLRUer           // In memory cacher for implicit teams
104	cardCache              *UserCardCache     // cache of keybase1.UserCard objects
105	fullSelfer             FullSelfer         // a loader that gets the full self object
106	pvlSource              MerkleStore        // a cache and fetcher for pvl
107	paramProofStore        MerkleStore        // a cache and fetcher for param proofs
108	externalURLStore       MerkleStore        // a cache and fetcher for external urls
109	PayloadCache           *PayloadCache      // cache of ChainLink payload json wrappers
110	kvRevisionCache        KVRevisionCacher   // cache of revisions for verifying key-value store results
111	Pegboard               *Pegboard
112
113	GpgClient        *GpgCLI        // A standard GPG-client (optional)
114	ShutdownHooks    []ShutdownHook // on shutdown, fire these...
115	SocketInfo       Socket         // which socket to bind/connect to
116	socketWrapperMu  *sync.RWMutex
117	SocketWrapper    *SocketWrapper    // only need one connection per
118	LoopbackListener *LoopbackListener // If we're in loopback mode, we'll connect through here
119	XStreams         *ExportedStreams  // a table of streams we've exported to the daemon (or vice-versa)
120	Timers           *TimerSet         // Which timers are currently configured on
121	UI               UI                // Interact with the UI
122	Service          bool              // whether we're in server mode
123	Standalone       bool              // whether we're launched as standalone command
124
125	shutdownOnce      *sync.Once         // whether we've shut down or not
126	ConnectionManager *ConnectionManager // keep tabs on all active client connections
127	NotifyRouter      *NotifyRouter      // How to route notifications
128	// How to route UIs. Nil if we're in standalone mode or in
129	// tests, and non-nil in service mode.
130	UIRouter           UIRouter                  // How to route UIs
131	proofServices      ExternalServicesCollector // All known external services
132	UIDMapper          UIDMapper                 // maps from UID to Usernames
133	ServiceMapper      ServiceSummaryMapper      // handles and caches batch requests for service summaries
134	ExitCode           keybase1.ExitCode         // Value to return to OS on Exit()
135	RateLimits         *RateLimits               // tracks the last time certain actions were taken
136	clockMu            *sync.Mutex               // protects Clock
137	clock              clockwork.Clock           // RealClock unless we're testing
138	secretStoreMu      *sync.Mutex               // protects secretStore
139	secretStore        *SecretStoreLocked        // SecretStore
140	hookMu             *sync.RWMutex             // protects loginHooks, logoutHooks
141	loginHooks         []LoginHook               // call these on login
142	logoutHooks        []NamedLogoutHook         // call these on logout
143	dbNukeHooks        []NamedDbNukeHook         // call these on dbnuke
144	GregorState        GregorState               // for dismissing gregor items that we've handled
145	GregorListener     GregorListener            // for alerting about clients connecting and registering UI protocols
146	oodiMu             *sync.RWMutex             // For manipulating the OutOfDateInfo
147	outOfDateInfo      *keybase1.OutOfDateInfo   // Stores out of date messages we got from API server headers.
148	lastUpgradeWarning *time.Time                // When the last upgrade was warned for (to reate-limit nagging)
149
150	uchMu               *sync.Mutex          // protects the UserChangedHandler array
151	UserChangedHandlers []UserChangedHandler // a list of handlers that deal generically with userchanged events
152	ConnectivityMonitor ConnectivityMonitor  // Detect whether we're connected or not.
153	localSigchainGuard  *LocalSigchainGuard  // Non-strict guard for shoeing away bg tasks when the user is doing sigchain actions
154	FeatureFlags        *FeatureFlagSet      // user's feature flag set
155
156	StandaloneChatConnector StandaloneChatConnector
157
158	// Can be overloaded by tests to get an improvement in performance
159	NewTriplesec func(pw []byte, salt []byte) (Triplesec, error)
160
161	// Options specified for testing only
162	TestOptions GlobalTestOptions
163
164	// Interface to get (cryptographically secure) randomness. Makes it easier
165	// to test randomized behaviors.
166	random Random
167
168	// It is threadsafe to call methods on ActiveDevice which will always be non-nil.
169	// But don't access its members directly. If you're going to be changing out the
170	// user (and resetting the ActiveDevice), then you should hold the switchUserMu
171	switchUserMu  *VerboseLock
172	ActiveDevice  *ActiveDevice
173	switchedUsers map[NormalizedUsername]bool // bookkeep users who have been switched over (and are still in secret store)
174
175	// OS Version passed from mobile native code. iOS and Android only.
176	// See go/bind/keybase.go
177	MobileOsVersion string
178	IsIPad          bool
179
180	SyncedContactList SyncedContactListProvider
181
182	GUIConfig *JSONFile
183
184	avatarLoader AvatarLoaderSource
185
186	TeamMemberCountCache *TeamMemberCountCache
187}
188
189type GlobalTestOptions struct {
190	NoBug3964Repair             bool
191	NoAutorotateOnBoxAuditRetry bool
192}
193
194func (g *GlobalContext) GetLog() logger.Logger                         { return g.Log }
195func (g *GlobalContext) GetPerfLog() logger.Logger                     { return g.PerfLog }
196func (g *GlobalContext) GetGUILogWriter() io.Writer                    { return g.GUILogFile }
197func (g *GlobalContext) GetVDebugLog() *VDebugLog                      { return g.VDL }
198func (g *GlobalContext) GetAPI() API                                   { return g.API }
199func (g *GlobalContext) GetExternalAPI() ExternalAPI                   { return g.XAPI }
200func (g *GlobalContext) GetServerURI() (string, error)                 { return g.Env.GetServerURI() }
201func (g *GlobalContext) GetEnv() *Env                                  { return g.Env }
202func (g *GlobalContext) GetDNSNameServerFetcher() DNSNameServerFetcher { return g.DNSNSFetcher }
203func (g *GlobalContext) GetKVStore() KVStorer                          { return g.LocalDb }
204func (g *GlobalContext) GetClock() clockwork.Clock                     { return g.Clock() }
205func (g *GlobalContext) GetEKLib() EKLib                               { return g.ekLib }
206func (g *GlobalContext) GetTeambotBotKeyer() TeambotBotKeyer           { return g.teambotBotKeyer }
207func (g *GlobalContext) GetTeambotMemberKeyer() TeambotMemberKeyer     { return g.teambotMemberKeyer }
208func (g *GlobalContext) GetProofServices() ExternalServicesCollector   { return g.proofServices }
209func (g *GlobalContext) GetAvatarLoader() AvatarLoaderSource           { return g.avatarLoader }
210
211func (g *GlobalContext) GetRandom() Random { return g.random }
212func (g *GlobalContext) SetRandom(r Random) {
213	if g.GetRunMode() != DevelRunMode {
214		panic("Random can only be altered in devel")
215	}
216	g.random = r
217}
218
219type LogGetter func() logger.Logger
220
221// Note: all these sync.Mutex fields are pointers so that the Clone funcs work.
222func NewGlobalContext() *GlobalContext {
223	log := logger.New("keybase")
224	ret := &GlobalContext{
225		Log:                log,
226		PerfLog:            log,
227		VDL:                NewVDebugLog(log),
228		SKBKeyringMu:       new(sync.Mutex),
229		perUserKeyringMu:   new(sync.Mutex),
230		vidMu:              new(sync.Mutex),
231		cacheMu:            new(sync.RWMutex),
232		socketWrapperMu:    new(sync.RWMutex),
233		shutdownOnce:       new(sync.Once),
234		clockMu:            new(sync.Mutex),
235		clock:              clockwork.NewRealClock(),
236		hookMu:             new(sync.RWMutex),
237		oodiMu:             new(sync.RWMutex),
238		outOfDateInfo:      &keybase1.OutOfDateInfo{},
239		lastUpgradeWarning: new(time.Time),
240		uchMu:              new(sync.Mutex),
241		secretStoreMu:      new(sync.Mutex),
242		NewTriplesec:       NewSecureTriplesec,
243		ActiveDevice:       NewActiveDevice(),
244		switchUserMu:       NewVerboseLock(VLog0, "switchUserMu"),
245		FeatureFlags:       NewFeatureFlagSet(),
246		switchedUsers:      make(map[NormalizedUsername]bool),
247		Pegboard:           NewPegboard(),
248		random:             &SecureRandom{},
249		RuntimeStats:       NewDummyRuntimeStats(),
250	}
251	ret.TeamMemberCountCache = newTeamMemberCountCache(ret)
252	return ret
253}
254
255func init() {
256}
257
258func (g *GlobalContext) SetCommandLine(cmd CommandLine) { g.Env.SetCommandLine(cmd) }
259
260func (g *GlobalContext) SetUI(u UI) { g.UI = u }
261
262func (g *GlobalContext) SetEKLib(ekLib EKLib) { g.ekLib = ekLib }
263
264func (g *GlobalContext) SetTeambotBotKeyer(keyer TeambotBotKeyer) { g.teambotBotKeyer = keyer }
265
266func (g *GlobalContext) SetTeambotMemberKeyer(keyer TeambotMemberKeyer) { g.teambotMemberKeyer = keyer }
267
268func (g *GlobalContext) initPerfLogFile() {
269	lfc := g.Env.GetLogFileConfig(g.Env.GetPerfLogFile())
270	lfc.SkipRedirectStdErr = true
271	lfw := logger.NewLogFileWriter(*lfc)
272	if err := lfw.Open(g.GetClock().Now()); err != nil {
273		g.Log.Debug("Unable to getLogger %v", err)
274		return
275	}
276	g.PerfLog = logger.NewInternalLogger(log.New(lfw, "", log.LstdFlags|log.Lmicroseconds|log.Lshortfile))
277}
278
279func (g *GlobalContext) initGUILogFile() {
280	config := g.Env.GetLogFileConfig(g.Env.GetGUILogFile())
281	config.SkipRedirectStdErr = true
282	fileWriter := logger.NewLogFileWriter(*config)
283	if err := fileWriter.Open(g.GetClock().Now()); err != nil {
284		g.GetLog().Debug("Unable to init GUI log file %v", err)
285		return
286	}
287	g.GUILogFile = fileWriter
288}
289
290func (g *GlobalContext) Init() *GlobalContext {
291	g.Env = NewEnv(nil, nil, g.GetLog)
292	g.Service = false
293	g.Resolver = NewResolverImpl()
294	g.RateLimits = NewRateLimits(g)
295	g.upakLoader = NewUncachedUPAKLoader(g)
296	g.teamLoader = newNullTeamLoader(g)
297	g.fastTeamLoader = newNullFastTeamLoader()
298	g.hiddenTeamChainManager = newNullHiddenTeamChainManager()
299	g.TeamRoleMapManager = newNullTeamRoleMapManager()
300	g.teamAuditor = newNullTeamAuditor()
301	g.teamBoxAuditor = newNullTeamBoxAuditor()
302	g.stellar = newNullStellar(g)
303	g.fullSelfer = NewUncachedFullSelf(g)
304	g.ConnectivityMonitor = NullConnectivityMonitor{}
305	g.localSigchainGuard = NewLocalSigchainGuard(g)
306	g.MobileNetState = NewMobileNetState(g)
307	g.MobileAppState = NewMobileAppState(g)
308	g.DesktopAppState = NewDesktopAppState(g)
309	g.RPCCanceler = NewRPCCanceler()
310	g.IdentifyDispatch = NewIdentifyDispatch()
311	g.Identify3State = NewIdentify3State(g)
312	g.GregorState = newNullGregorState()
313	g.LocalNetworkInstrumenterStorage = NewDiskInstrumentationStorage(g, keybase1.NetworkSource_LOCAL)
314	g.RemoteNetworkInstrumenterStorage = NewDiskInstrumentationStorage(g, keybase1.NetworkSource_REMOTE)
315
316	g.Log.Debug("GlobalContext#Init(%p)\n", g)
317
318	return g
319}
320
321func NewGlobalContextInit() *GlobalContext {
322	return NewGlobalContext().Init()
323}
324
325func (g *GlobalContext) SetService() {
326	g.Service = true
327	g.ConnectionManager = NewConnectionManager()
328	g.NotifyRouter = NewNotifyRouter(g)
329}
330
331func (g *GlobalContext) SetUIDMapper(u UIDMapper) {
332	g.UIDMapper = u
333}
334
335func (g *GlobalContext) SetServiceSummaryMapper(u ServiceSummaryMapper) {
336	g.ServiceMapper = u
337}
338
339func (g *GlobalContext) SetUIRouter(u UIRouter) {
340	g.UIRouter = u
341}
342
343func (g *GlobalContext) SetDNSNameServerFetcher(d DNSNameServerFetcher) {
344	g.DNSNSFetcher = d
345}
346
347func (g *GlobalContext) SetUPAKLoader(u UPAKLoader) {
348	g.upakLoader = u
349}
350
351func (g *GlobalContext) SetAvatarLoader(a AvatarLoaderSource) {
352	g.avatarLoader = a
353}
354
355// simulateServiceRestart simulates what happens when a service restarts for the
356// purposes of testing.
357func (g *GlobalContext) simulateServiceRestart() {
358	defer g.switchUserMu.Acquire(NewMetaContext(context.TODO(), g), "simulateServiceRestart")()
359	_ = g.ActiveDevice.Clear()
360}
361
362// ConfigureLogging should be given non-nil Usage if called by the main
363// service.
364func (g *GlobalContext) ConfigureLogging(usage *Usage) error {
365	style := g.Env.GetLogFormat()
366	debug := g.Env.GetDebug()
367
368	logFile, ok := g.Env.GetEffectiveLogFile()
369	// Configure regardless if the logFile should be used or not
370	g.Log.Configure(style, debug, logFile)
371
372	// Start redirecting logs if the logFile should be used
373	// If this is not called, prints logs to stdout.
374	if ok {
375		err := logger.SetLogFileConfig(g.Env.GetLogFileConfig(logFile), &logger.BufferedLoggerConfig{
376			Frequency: 200 * time.Millisecond,
377			Size:      1000000,
378		})
379		if err != nil {
380			return err
381		}
382	}
383	g.VDL.Configure(g.Env.GetVDebugSetting())
384
385	// On Linux, the post-install script calls `keybase --use-root-config-file
386	// config get --direct` to figure out if the redirector should be enabled or not.
387	// That command, like all other commands, goes through all these initial steps
388	// like ConfigureLogging before executing the command. On Ubuntu, '$HOME' is *not*
389	// changed to the root's user's HOME when using sudo, which basically means
390	// that `sudo bash -c 'mkdir $HOME/.cache'`, e.g., creates it within the *user's*
391	// home directory with root permissions (unlike Debian, Fedora, Arch, etc.).
392	// So, in this case, we do not configure the log files so as not to mess up
393	// permissions in the user's home directory.
394	shouldInitLogs := true
395	if usage != nil && usage.AllowRoot {
396		isAdmin, _, err := IsSystemAdminUser()
397		if err == nil && isAdmin {
398			shouldInitLogs = false
399		}
400	}
401
402	if shouldInitLogs {
403		g.initGUILogFile()
404		g.initPerfLogFile()
405	}
406
407	return nil
408}
409
410func (g *GlobalContext) PushShutdownHook(sh ShutdownHook) {
411	g.ShutdownHooks = append(g.ShutdownHooks, sh)
412}
413
414func (g *GlobalContext) ConfigureConfig() error {
415	c := NewJSONConfigFile(g, g.Env.GetConfigFilename())
416	err := c.Load(false)
417	if err != nil {
418		return err
419	}
420	if err = c.Check(); err != nil {
421		return err
422	}
423	g.Env.SetConfig(c, c)
424	return nil
425}
426
427func (g *GlobalContext) ConfigReload() error {
428	err := g.ConfigureConfig()
429	if err != nil {
430		return err
431	}
432	guiConfigErr := g.ConfigureGUIConfig()
433	if guiConfigErr != nil {
434		g.Log.Debug("Failed to open gui config: %s", guiConfigErr)
435	}
436	return g.ConfigureUpdaterConfig()
437}
438
439// migrateGUIConfig does not delete old values from service's config.
440func migrateGUIConfig(serviceConfig ConfigReader, guiConfig *JSONFile) error {
441	var errs []error
442
443	p := "ui.routeState2"
444	if uiRouteState2, isSet := serviceConfig.GetStringAtPath(p); isSet {
445		if err := guiConfig.SetStringAtPath(p, uiRouteState2); err != nil {
446			errs = append(errs, err)
447		}
448	}
449
450	p = "ui.shownMonsterPushPrompt"
451	if uiMonsterStorage, isSet := serviceConfig.GetBoolAtPath(p); isSet {
452		if err := guiConfig.SetBoolAtPath(p, uiMonsterStorage); err != nil {
453			errs = append(errs, err)
454		}
455	}
456
457	p = "stellar.lastSentXLM"
458	if stellarLastSentXLM, isSet := serviceConfig.GetBoolAtPath(p); isSet {
459		if err := guiConfig.SetBoolAtPath(p, stellarLastSentXLM); err != nil {
460			errs = append(errs, err)
461		}
462	}
463
464	p = "ui.importContacts"
465	syncSettings, err := serviceConfig.GetInterfaceAtPath(p)
466	if err != nil {
467		if !isJSONNoSuchKeyError(err) {
468			errs = append(errs, err)
469		}
470	} else {
471		syncSettings, ok := syncSettings.(map[string]interface{})
472		if !ok {
473			errs = append(errs, fmt.Errorf("Failed to coerce ui.importContacts in migration"))
474		} else {
475			for username, syncEnabled := range syncSettings {
476				syncEnabled, ok := syncEnabled.(bool)
477				if !ok {
478					errs = append(errs, fmt.Errorf("Failed to coerce syncEnabled in migration for %s", username))
479				}
480				err := guiConfig.SetBoolAtPath(fmt.Sprintf("%s.%s", p, username), syncEnabled)
481				if err != nil {
482					errs = append(errs, err)
483				}
484			}
485		}
486	}
487	return CombineErrors(errs...)
488}
489
490func (g *GlobalContext) ConfigureGUIConfig() error {
491	guiConfig := NewJSONFile(g, g.Env.GetGUIConfigFilename(), "gui config")
492	found, err := guiConfig.LoadCheckFound()
493	if err == nil {
494		if !found {
495			err := guiConfig.SetBoolAtPath("gui", true)
496			if err != nil {
497				return err
498			}
499			// If this is the first time creating this file, manually migrate
500			// old GUI config values from the main config file best-effort.
501			serviceConfig := g.Env.GetConfig()
502			if migrateErr := migrateGUIConfig(serviceConfig, guiConfig); migrateErr != nil {
503				g.Log.Debug("Failed to migrate config to new GUI config file: %s", migrateErr)
504			}
505
506		}
507		g.Env.SetGUIConfig(guiConfig)
508	}
509	return err
510}
511
512func (g *GlobalContext) ConfigureUpdaterConfig() error {
513	c := NewJSONUpdaterConfigFile(g)
514	err := c.Load(false)
515	if err == nil {
516		g.Env.SetUpdaterConfig(c)
517	} else {
518		g.Log.Debug("Failed to open update config: %s\n", err)
519	}
520	return err
521}
522
523func (g *GlobalContext) ConfigureTimers() error {
524	g.Timers = NewTimerSet(g)
525	return nil
526}
527
528func (g *GlobalContext) ConfigureKeyring() error {
529	g.Keyrings = NewKeyrings(g)
530	return nil
531}
532
533func VersionMessage(linefn func(string)) {
534	linefn(fmt.Sprintf("Keybase CLI %s", VersionString()))
535	linefn(fmt.Sprintf("- Built with %s", runtime.Version()))
536	linefn("- Visit https://keybase.io for more details")
537}
538
539func (g *GlobalContext) StartupMessage() {
540	VersionMessage(func(s string) { g.Log.Debug(s) })
541}
542
543func (g *GlobalContext) ConfigureAPI() error {
544	iapi, xapi, err := NewAPIEngines(g)
545	if err != nil {
546		return fmt.Errorf("Failed to configure API access: %s", err)
547	}
548	g.API = iapi
549	g.XAPI = xapi
550	return nil
551}
552
553// shutdownCachesLocked shutdown any non-nil caches that have running goroutines
554// in them. It can be called from either configureMemCachesLocked (via logout or flush),
555// or via Shutdown. In either case, callers must hold g.cacheMu.
556func (g *GlobalContext) shutdownCachesLocked() {
557
558	// shutdown and nil out any existing caches.
559	if g.trackCache != nil {
560		g.trackCache.Shutdown()
561	}
562	if g.identify2Cache != nil {
563		g.identify2Cache.Shutdown()
564	}
565	if g.linkCache != nil {
566		g.linkCache.Shutdown()
567	}
568	if g.cardCache != nil {
569		g.cardCache.Shutdown()
570	}
571}
572
573func (g *GlobalContext) TrackCache() *TrackCache {
574	g.cacheMu.Lock()
575	defer g.cacheMu.Unlock()
576	return g.trackCache
577}
578
579func (g *GlobalContext) Identify2Cache() Identify2Cacher {
580	g.cacheMu.Lock()
581	defer g.cacheMu.Unlock()
582	return g.identify2Cache
583}
584
585func (g *GlobalContext) CardCache() *UserCardCache {
586	g.cacheMu.Lock()
587	defer g.cacheMu.Unlock()
588	return g.cardCache
589}
590
591func (g *GlobalContext) LinkCache() *LinkCache {
592	g.cacheMu.Lock()
593	defer g.cacheMu.Unlock()
594	return g.linkCache
595}
596
597func (g *GlobalContext) configureMemCachesLocked(isFlush bool) {
598
599	g.shutdownCachesLocked()
600
601	g.IDLocktab = NewLockTable()
602	g.loadUserLockTab = NewLockTable()
603	g.Resolver.EnableCaching(NewMetaContextBackground(g))
604	g.trackCache = NewTrackCache()
605	g.identify2Cache = NewIdentify2Cache(g.Env.GetUserCacheMaxAge())
606	g.Log.Debug("Created Identify2Cache, max age: %s", g.Env.GetUserCacheMaxAge())
607
608	g.linkCache = NewLinkCache(g.Env.GetLinkCacheSize(), g.Env.GetLinkCacheCleanDur())
609	g.Log.Debug("Created LinkCache, max size: %d, clean dur: %s", g.Env.GetLinkCacheSize(), g.Env.GetLinkCacheCleanDur())
610	g.cardCache = NewUserCardCache(g.Env.GetUserCacheMaxAge())
611	g.Log.Debug("Created CardCache, max age: %s", g.Env.GetUserCacheMaxAge())
612
613	// If we're just flushing the caches, and already have a Proof cache, then the right idea
614	// is just to reset what's in the ProofCache. Otherwise, we make a new one.
615	if isFlush && g.ProofCache != nil {
616		_ = g.ProofCache.Reset()
617	} else {
618		g.ProofCache = NewProofCache(g, g.Env.GetProofCacheSize())
619	}
620
621	// If it's startup (and not a "flush"), then install a new full selfer
622	// cache. Otherwise, just make a new instance of the kind that's already there.
623	if isFlush {
624		g.fullSelfer = g.fullSelfer.New()
625	} else {
626		g.fullSelfer = NewCachedFullSelf(g)
627	}
628
629	g.Log.Debug("made a new full self cache")
630	g.upakLoader = NewCachedUPAKLoader(g, CachedUserTimeout)
631	g.Log.Debug("made a new cached UPAK loader (timeout=%v)", CachedUserTimeout)
632	g.PayloadCache = NewPayloadCache(g, g.Env.GetPayloadCacheSize())
633}
634
635func (g *GlobalContext) ConfigureCaches() error {
636	g.cacheMu.Lock()
637	defer g.cacheMu.Unlock()
638	g.configureMemCachesLocked(false)
639	return g.configureDiskCachesLocked()
640}
641
642func (g *GlobalContext) FlushCaches() {
643	g.cacheMu.Lock()
644	defer g.cacheMu.Unlock()
645	g.configureMemCachesLocked(true)
646	g.TeamRoleMapManager.FlushCache()
647}
648
649func (g *GlobalContext) configureDiskCachesLocked() error {
650	// We consider the local DBs as caches; they're caching our
651	// fetches from the server after all (and also our cryptographic
652	// checking).
653	g.LocalDb = NewJSONLocalDb(NewLevelDb(g, g.Env.GetDbFilename))
654	g.LocalChatDb = NewJSONLocalDb(NewLevelDb(g, g.Env.GetChatDbFilename))
655
656	epick := FirstErrorPicker{}
657	epick.Push(g.LocalDb.Open())
658	epick.Push(g.LocalChatDb.Open())
659	return epick.Error()
660}
661
662func (g *GlobalContext) ConfigureMerkleClient() error {
663	g.MerkleClient = NewMerkleClient(g)
664	return nil
665}
666
667func (g *GlobalContext) GetUPAKLoader() UPAKLoader {
668	g.cacheMu.RLock()
669	defer g.cacheMu.RUnlock()
670	return g.upakLoader
671}
672
673func (g *GlobalContext) GetTeamLoader() TeamLoader {
674	g.cacheMu.RLock()
675	defer g.cacheMu.RUnlock()
676	return g.teamLoader
677}
678
679func (g *GlobalContext) GetFastTeamLoader() FastTeamLoader {
680	g.cacheMu.RLock()
681	defer g.cacheMu.RUnlock()
682	return g.fastTeamLoader
683}
684
685func (g *GlobalContext) GetHiddenTeamChainManager() HiddenTeamChainManager {
686	g.cacheMu.RLock()
687	defer g.cacheMu.RUnlock()
688	return g.hiddenTeamChainManager
689}
690
691func (g *GlobalContext) GetTeamRoleMapManager() TeamRoleMapManager {
692	g.cacheMu.RLock()
693	defer g.cacheMu.RUnlock()
694	return g.TeamRoleMapManager
695}
696
697func (g *GlobalContext) SetTeamRoleMapManager(r TeamRoleMapManager) {
698	g.cacheMu.Lock()
699	defer g.cacheMu.Unlock()
700	g.TeamRoleMapManager = r
701}
702
703func (g *GlobalContext) SetHiddenTeamChainManager(h HiddenTeamChainManager) {
704	g.cacheMu.Lock()
705	defer g.cacheMu.Unlock()
706	g.hiddenTeamChainManager = h
707}
708
709func (g *GlobalContext) GetTeamAuditor() TeamAuditor {
710	g.cacheMu.RLock()
711	defer g.cacheMu.RUnlock()
712	return g.teamAuditor
713}
714
715func (g *GlobalContext) GetTeamBoxAuditor() TeamBoxAuditor {
716	g.cacheMu.RLock()
717	defer g.cacheMu.RUnlock()
718	return g.teamBoxAuditor
719}
720
721func (g *GlobalContext) GetStellar() Stellar {
722	g.cacheMu.RLock()
723	defer g.cacheMu.RUnlock()
724	return g.stellar
725}
726
727func (g *GlobalContext) GetDeviceEKStorage() DeviceEKStorage {
728	g.cacheMu.RLock()
729	defer g.cacheMu.RUnlock()
730	return g.deviceEKStorage
731}
732
733func (g *GlobalContext) GetUserEKBoxStorage() UserEKBoxStorage {
734	g.cacheMu.RLock()
735	defer g.cacheMu.RUnlock()
736	return g.userEKBoxStorage
737}
738
739func (g *GlobalContext) GetTeamEKBoxStorage() TeamEKBoxStorage {
740	g.cacheMu.RLock()
741	defer g.cacheMu.RUnlock()
742	return g.teamEKBoxStorage
743}
744
745func (g *GlobalContext) GetTeambotEKBoxStorage() TeamEKBoxStorage {
746	g.cacheMu.RLock()
747	defer g.cacheMu.RUnlock()
748	return g.teambotEKBoxStorage
749}
750
751func (g *GlobalContext) GetImplicitTeamConflictInfoCacher() LRUer {
752	g.cacheMu.RLock()
753	defer g.cacheMu.RUnlock()
754	return g.itciCacher
755}
756
757func (g *GlobalContext) SetImplicitTeamConflictInfoCacher(l LRUer) {
758	g.cacheMu.RLock()
759	defer g.cacheMu.RUnlock()
760	g.itciCacher = l
761}
762
763func (g *GlobalContext) GetImplicitTeamCacher() MemLRUer {
764	g.cacheMu.RLock()
765	defer g.cacheMu.RUnlock()
766	return g.iteamCacher
767}
768
769func (g *GlobalContext) SetImplicitTeamCacher(l MemLRUer) {
770	g.cacheMu.RLock()
771	defer g.cacheMu.RUnlock()
772	g.iteamCacher = l
773}
774
775func (g *GlobalContext) GetKVRevisionCache() KVRevisionCacher {
776	g.cacheMu.RLock()
777	defer g.cacheMu.RUnlock()
778	return g.kvRevisionCache
779}
780
781func (g *GlobalContext) SetKVRevisionCache(kvr KVRevisionCacher) {
782	g.cacheMu.RLock()
783	defer g.cacheMu.RUnlock()
784	g.kvRevisionCache = kvr
785}
786
787func (g *GlobalContext) GetFullSelfer() FullSelfer {
788	g.cacheMu.RLock()
789	defer g.cacheMu.RUnlock()
790	return g.fullSelfer
791}
792
793func (g *GlobalContext) GetParamProofStore() MerkleStore {
794	return g.paramProofStore
795}
796
797func (g *GlobalContext) GetExternalURLStore() MerkleStore {
798	return g.externalURLStore
799}
800
801// to implement ProofContext
802func (g *GlobalContext) GetPvlSource() MerkleStore {
803	return g.pvlSource
804}
805
806// to implement ProofContext
807func (g *GlobalContext) GetAppType() AppType {
808	return g.Env.GetAppType()
809}
810
811func (g *GlobalContext) IsMobileAppType() bool {
812	return g.Env.GetAppType() == MobileAppType
813}
814
815func (g *GlobalContext) ConfigureExportedStreams() error {
816	g.XStreams = NewExportedStreams()
817	return nil
818}
819
820// Shutdown is called exactly once per-process and does whatever
821// cleanup is necessary to shut down the server.
822func (g *GlobalContext) Shutdown(mctx MetaContext) error {
823	var err error
824	didShutdown := false
825
826	// Wrap in a Once.Do so that we don't inadvertedly
827	// run this code twice.
828	g.shutdownOnce.Do(func() {
829		g.Log.Debug("GlobalContext#Shutdown(%p)\n", g)
830		if g.PerfLog != nil {
831			g.PerfLog.Debug("GlobalContext#Shutdown(%p)\n", g)
832		}
833		didShutdown = true
834
835		epick := FirstErrorPicker{}
836
837		if g.hiddenTeamChainManager != nil {
838			g.hiddenTeamChainManager.Shutdown(mctx)
839		}
840
841		if g.NotifyRouter != nil {
842			g.NotifyRouter.Shutdown()
843		}
844
845		if g.UIRouter != nil {
846			g.UIRouter.Shutdown()
847		}
848
849		if g.ConnectionManager != nil {
850			g.ConnectionManager.Shutdown()
851		}
852
853		if g.UI != nil {
854			epick.Push(g.UI.Shutdown())
855		}
856
857		// Shutdown can still race with Logout, so make sure that we hold onto
858		// the cacheMu before shutting down the caches. See comments in
859		// shutdownCachesLocked
860		g.cacheMu.Lock()
861		g.shutdownCachesLocked()
862		g.cacheMu.Unlock()
863
864		if g.proofServices != nil {
865			g.proofServices.Shutdown()
866		}
867
868		if g.Resolver != nil {
869			g.Resolver.Shutdown(NewMetaContextBackground(g))
870		}
871
872		g.Log.Debug("executing %d shutdown hooks; errCount=%d", len(g.ShutdownHooks), epick.Count())
873		for _, hook := range g.ShutdownHooks {
874			epick.Push(hook(mctx))
875		}
876		g.Log.Debug("executed shutdown hooks; errCount=%d", epick.Count())
877
878		if g.LocalNetworkInstrumenterStorage != nil {
879			<-g.LocalNetworkInstrumenterStorage.Stop(mctx.Ctx())
880		}
881
882		if g.RemoteNetworkInstrumenterStorage != nil {
883			<-g.RemoteNetworkInstrumenterStorage.Stop(mctx.Ctx())
884		}
885
886		// shutdown the databases after the shutdown hooks run, we may want to
887		// flush memory caches to disk during shutdown.
888		if g.LocalDb != nil {
889			epick.Push(g.LocalDb.Close())
890		}
891		if g.LocalChatDb != nil {
892			epick.Push(g.LocalChatDb.Close())
893		}
894		if g.GUILogFile != nil {
895			epick.Push(g.GUILogFile.Close())
896		}
897		<-g.Identify3State.Shutdown()
898
899		err = epick.Error()
900
901		g.Log.Debug("exiting shutdown code=%d; errCount=%d; firstErr=%v", g.ExitCode, epick.Count(), err)
902	})
903
904	// Make a little bit of a statement if we wind up here a second time
905	// (which is a bug).
906	if !didShutdown {
907		g.Log.Debug("Skipped shutdown on second call")
908	}
909
910	return err
911}
912
913func (u Usage) UseKeyring() bool {
914	return u.KbKeyring || u.GpgKeyring
915}
916
917// If changed, make sure to correct standalone usage in g.Configure below
918var ServiceUsage = Usage{
919	Config:     true,
920	KbKeyring:  true,
921	GpgKeyring: true,
922	API:        true,
923	Socket:     true,
924}
925
926func (g *GlobalContext) ConfigureCommand(line CommandLine, cmd Command) error {
927	usage := cmd.GetUsage()
928	return g.Configure(line, usage)
929}
930
931func (g *GlobalContext) Configure(line CommandLine, usage Usage) error {
932	g.SetCommandLine(line)
933
934	if err := g.ConfigureLogging(&usage); err != nil {
935		return err
936	}
937	if g.Env.GetStandalone() {
938		// If standalone, override the usage to be the same as in a service
939		// If changed, make sure to correct ServiceUsage above.
940		usage.Config = ServiceUsage.Config
941		usage.KbKeyring = ServiceUsage.KbKeyring
942		usage.GpgKeyring = ServiceUsage.GpgKeyring
943		usage.API = ServiceUsage.API
944		usage.Socket = ServiceUsage.Socket
945	}
946
947	if err := g.ConfigureUsage(usage); err != nil {
948		return err
949	}
950
951	// secretStore must be created after SetCommandLine and ConfigureUsage in
952	// order to correctly use -H,-home flag and config vars for
953	// remember_passphrase.
954	g.secretStoreMu.Lock()
955	g.secretStore = NewSecretStoreLocked(NewMetaContextBackground(g))
956	g.secretStoreMu.Unlock()
957
958	return nil
959}
960
961func (g *GlobalContext) ConfigureUsage(usage Usage) error {
962	var err error
963
964	if usage.Config {
965		if err = g.ConfigReload(); err != nil {
966			return err
967		}
968	}
969	if usage.UseKeyring() {
970		if err = g.ConfigureKeyring(); err != nil {
971			return err
972		}
973	}
974	if usage.API {
975		if err = g.ConfigureAPI(); err != nil {
976			return err
977		}
978	}
979	if usage.Socket || !g.Env.GetStandalone() {
980		if err = g.ConfigureSocketInfo(); err != nil {
981			return err
982		}
983	}
984	if err = g.ConfigureExportedStreams(); err != nil {
985		return err
986	}
987
988	if err = g.ConfigureCaches(); err != nil {
989		return err
990	}
991	g.LocalNetworkInstrumenterStorage.Start(context.TODO())
992	g.RemoteNetworkInstrumenterStorage.Start(context.TODO())
993
994	if err = g.ConfigureMerkleClient(); err != nil {
995		return err
996	}
997
998	if g.UI != nil {
999		if err = g.UI.Configure(); err != nil {
1000			return err
1001		}
1002	}
1003
1004	return g.ConfigureTimers()
1005}
1006
1007func (g *GlobalContext) GetGpgClient() *GpgCLI {
1008	if g.GpgClient == nil {
1009		g.GpgClient = NewGpgCLI(g, nil)
1010	}
1011	return g.GpgClient
1012}
1013
1014func (g *GlobalContext) GetMyUID() keybase1.UID {
1015	// Prefer ActiveDevice, that's the prefered way
1016	// to figure out what the current user's UID is.
1017	uid := g.ActiveDevice.UID()
1018	if uid.Exists() {
1019		return uid
1020	}
1021	return g.Env.GetUID()
1022}
1023
1024func (g *GlobalContext) ConfigureSocketInfo() (err error) {
1025	g.SocketInfo, err = NewSocket(g)
1026	return err
1027}
1028
1029// Contextified objects have explicit references to the GlobalContext,
1030// so that G can be swapped out for something else.  We're going to incrementally
1031// start moving objects over to this system.
1032type Contextified struct {
1033	g *GlobalContext
1034}
1035
1036func (c Contextified) G() *GlobalContext {
1037	return c.g
1038}
1039
1040func (c Contextified) MetaContext(ctx context.Context) MetaContext {
1041	return NewMetaContext(ctx, c.g)
1042}
1043
1044func (c Contextified) GStrict() *GlobalContext {
1045	return c.g
1046}
1047
1048func (c *Contextified) SetGlobalContext(g *GlobalContext) { c.g = g }
1049
1050func NewContextified(gc *GlobalContext) Contextified {
1051	return Contextified{g: gc}
1052}
1053
1054type Contextifier interface {
1055	G() *GlobalContext
1056}
1057
1058func (g *GlobalContext) GetConfiguredAccounts(ctx context.Context) ([]keybase1.ConfiguredAccount, error) {
1059	m := NewMetaContext(ctx, g)
1060	g.secretStoreMu.Lock()
1061	defer g.secretStoreMu.Unlock()
1062	return GetConfiguredAccounts(m, g.secretStore)
1063}
1064
1065func (g *GlobalContext) GetAllUserNames() (NormalizedUsername, []NormalizedUsername, error) {
1066	return g.Env.GetConfig().GetAllUsernames()
1067}
1068
1069func (g *GlobalContext) GetStoredSecretServiceName() string {
1070	return g.Env.GetStoredSecretServiceName()
1071}
1072
1073func (g *GlobalContext) GetStoredSecretAccessGroup() string {
1074	return g.Env.GetStoredSecretAccessGroup()
1075}
1076
1077func (g *GlobalContext) GetUsersWithStoredSecrets(ctx context.Context) ([]string, error) {
1078	g.secretStoreMu.Lock()
1079	defer g.secretStoreMu.Unlock()
1080	if g.secretStore != nil {
1081		return g.secretStore.GetUsersWithStoredSecrets(NewMetaContext(ctx, g))
1082	}
1083	return []string{}, nil
1084}
1085
1086func (g *GlobalContext) GetCacheDir() string {
1087	return g.Env.GetCacheDir()
1088}
1089
1090func (g *GlobalContext) GetSharedCacheDir() string {
1091	return g.Env.GetSharedCacheDir()
1092}
1093
1094func (g *GlobalContext) GetRuntimeDir() string {
1095	return g.Env.GetRuntimeDir()
1096}
1097
1098func (g *GlobalContext) GetRunMode() RunMode {
1099	return g.Env.GetRunMode()
1100}
1101
1102func (g *GlobalContext) Clock() clockwork.Clock {
1103	g.clockMu.Lock()
1104	defer g.clockMu.Unlock()
1105	if g.clock == nil {
1106		g.clock = clockwork.NewRealClock()
1107	}
1108	return g.clock
1109}
1110
1111func (g *GlobalContext) SetClock(c clockwork.Clock) {
1112	g.clockMu.Lock()
1113	defer g.clockMu.Unlock()
1114	g.clock = c
1115}
1116
1117func (g *GlobalContext) GetMyClientDetails() keybase1.ClientDetails {
1118	return keybase1.ClientDetails{
1119		ClientType: keybase1.ClientType_CLI,
1120		Pid:        os.Getpid(),
1121		Argv:       os.Args,
1122		Version:    VersionString(),
1123	}
1124}
1125
1126type UnforwardedLoggerWithLegacyInterface interface {
1127	Debug(s string, args ...interface{})
1128	Error(s string, args ...interface{})
1129	Errorf(s string, args ...interface{})
1130	Warning(s string, args ...interface{})
1131	Info(s string, args ...interface{})
1132	Profile(s string, args ...interface{})
1133}
1134
1135func (g *GlobalContext) GetUnforwardedLogger() (log UnforwardedLoggerWithLegacyInterface) {
1136	defer func() {
1137		if log == nil {
1138			// Hopefully this won't happen before we get to refactor the logger
1139			// interfaces. If this happens, we really shouldn't return nil, but
1140			// rather fix whatever caused it.
1141			panic("can't make unforwarded logger")
1142		}
1143	}()
1144	if g.Log == nil {
1145		return nil
1146	}
1147	if log, ok := g.Log.(*logger.Standard); ok {
1148		return (*logger.UnforwardedLogger)(log)
1149	}
1150	if log, ok := g.Log.(*logger.TestLogger); ok {
1151		return log
1152	}
1153	return nil
1154}
1155
1156// GetLogf returns a logger with a minimal formatter style interface
1157func (g *GlobalContext) GetLogf() logger.Loggerf {
1158	return logger.NewLoggerf(g.Log)
1159}
1160
1161func (g *GlobalContext) AddLoginHook(hook LoginHook) {
1162	g.hookMu.Lock()
1163	defer g.hookMu.Unlock()
1164	g.Log.Debug("AddLoginHook: %T", hook)
1165	g.loginHooks = append(g.loginHooks, hook)
1166}
1167
1168func (g *GlobalContext) CallLoginHooks(mctx MetaContext) {
1169	mctx.Debug("G#CallLoginHooks")
1170
1171	// Trigger the creation of a per-user-keyring
1172	_, _ = g.GetPerUserKeyring(mctx.Ctx())
1173
1174	mctx.Debug("CallLoginHooks: running UPAK#LoginAs")
1175	err := g.GetUPAKLoader().LoginAs(mctx.CurrentUID())
1176	if err != nil {
1177		mctx.Warning("LoginAs error: %+v", err)
1178	}
1179
1180	// Do so outside the lock below
1181	mctx.Debug("CallLoginHooks: running FullSelfer#OnLogin")
1182	err = g.GetFullSelfer().OnLogin(mctx)
1183	if err != nil {
1184		mctx.Warning("OnLogin full self error: %+v", err)
1185	}
1186
1187	mctx.Debug("CallLoginHooks: recording login in secretstore")
1188	err = RecordLoginTime(mctx, g.Env.GetUsername())
1189	if err != nil {
1190		mctx.Warning("OnLogin RecordLogin error: %+v", err)
1191	}
1192
1193	mctx.Debug("CallLoginHooks: running registered login hooks")
1194	g.hookMu.RLock()
1195	defer g.hookMu.RUnlock()
1196	for _, h := range g.loginHooks {
1197		mctx.Debug("CallLoginHooks: will call login hook for %T", h)
1198	}
1199	for _, h := range g.loginHooks {
1200		mctx.Debug("CallLoginHooks: calling login hook for %T", h)
1201		if err := h.OnLogin(mctx); err != nil {
1202			mctx.Warning("OnLogin hook error: %s", err)
1203		}
1204	}
1205}
1206
1207type NamedLogoutHook struct {
1208	LogoutHook
1209	name string
1210}
1211
1212func (g *GlobalContext) AddLogoutHook(hook LogoutHook, name string) {
1213	g.hookMu.Lock()
1214	defer g.hookMu.Unlock()
1215	g.logoutHooks = append(g.logoutHooks, NamedLogoutHook{
1216		LogoutHook: hook,
1217		name:       name,
1218	})
1219}
1220
1221func (g *GlobalContext) CallLogoutHooks(mctx MetaContext) {
1222	defer mctx.Trace("GlobalContext.CallLogoutHooks", nil)()
1223	g.hookMu.RLock()
1224	defer g.hookMu.RUnlock()
1225	for _, h := range g.logoutHooks {
1226		mctx.Debug("+ Logout hook [%v]", h.name)
1227		if err := h.OnLogout(mctx); err != nil {
1228			mctx.Warning("| Logout hook [%v] : %s", h.name, err)
1229		}
1230		mctx.Debug("- Logout hook [%v]", h.name)
1231	}
1232}
1233
1234type NamedDbNukeHook struct {
1235	DbNukeHook
1236	name string
1237}
1238
1239func (g *GlobalContext) AddDbNukeHook(hook DbNukeHook, name string) {
1240	g.hookMu.Lock()
1241	defer g.hookMu.Unlock()
1242	g.dbNukeHooks = append(g.dbNukeHooks, NamedDbNukeHook{
1243		DbNukeHook: hook,
1244		name:       name,
1245	})
1246}
1247
1248func (g *GlobalContext) CallDbNukeHooks(mctx MetaContext) {
1249	defer mctx.Trace("GlobalContext.CallDbNukeHook", nil)()
1250	g.hookMu.RLock()
1251	defer g.hookMu.RUnlock()
1252	for _, h := range g.dbNukeHooks {
1253		mctx.Debug("+ DbNukeHook hook [%v]", h.name)
1254		if err := h.OnDbNuke(mctx); err != nil {
1255			mctx.Warning("| DbNukeHook hook [%v] : %s", h.name, err)
1256		}
1257		mctx.Debug("- DbNukeHook hook [%v]", h.name)
1258	}
1259}
1260
1261func (g *GlobalContext) GetConfigDir() string {
1262	return g.Env.GetConfigDir()
1263}
1264
1265func (g *GlobalContext) GetMountDir() (string, error) {
1266	return g.Env.GetMountDir()
1267}
1268
1269// GetServiceInfoPath returns path to info file written by the Keybase service after startup
1270func (g *GlobalContext) GetServiceInfoPath() string {
1271	return g.Env.GetServiceInfoPath()
1272}
1273
1274// GetKBFSInfoPath returns path to info file written by the KBFS service after startup
1275func (g *GlobalContext) GetKBFSInfoPath() string {
1276	return g.Env.GetKBFSInfoPath()
1277}
1278
1279func (g *GlobalContext) GetLogDir() string {
1280	return g.Env.GetLogDir()
1281}
1282
1283func (g *GlobalContext) GetDataDir() string {
1284	return g.Env.GetDataDir()
1285}
1286
1287func (g *GlobalContext) NewRPCLogFactory() *RPCLogFactory {
1288	return &RPCLogFactory{Contextified: NewContextified(g)}
1289}
1290
1291func (g *GlobalContext) MakeAssertionContext(mctx MetaContext) AssertionContext {
1292	g.cacheMu.Lock()
1293	defer g.cacheMu.Unlock()
1294	if g.proofServices == nil {
1295		return nil
1296	}
1297	return MakeAssertionContext(mctx, g.proofServices)
1298}
1299
1300func (g *GlobalContext) SetProofServices(s ExternalServicesCollector) {
1301	g.cacheMu.Lock()
1302	defer g.cacheMu.Unlock()
1303	g.proofServices = s
1304}
1305
1306func (g *GlobalContext) SetParamProofStore(s MerkleStore) {
1307	g.cacheMu.Lock()
1308	defer g.cacheMu.Unlock()
1309	g.paramProofStore = s
1310}
1311
1312func (g *GlobalContext) SetExternalURLStore(s MerkleStore) {
1313	g.cacheMu.Lock()
1314	defer g.cacheMu.Unlock()
1315	g.externalURLStore = s
1316}
1317
1318func (g *GlobalContext) SetPvlSource(s MerkleStore) {
1319	g.cacheMu.Lock()
1320	defer g.cacheMu.Unlock()
1321	g.pvlSource = s
1322}
1323
1324func (g *GlobalContext) SetTeamLoader(l TeamLoader) {
1325	g.cacheMu.Lock()
1326	defer g.cacheMu.Unlock()
1327	g.teamLoader = l
1328}
1329
1330func (g *GlobalContext) SetMerkleClient(m MerkleClientInterface) {
1331	g.cacheMu.Lock()
1332	defer g.cacheMu.Unlock()
1333	g.MerkleClient = m
1334}
1335
1336func (g *GlobalContext) GetMerkleClient() MerkleClientInterface {
1337	g.cacheMu.Lock()
1338	defer g.cacheMu.Unlock()
1339	return g.MerkleClient
1340}
1341
1342func (g *GlobalContext) SetFastTeamLoader(l FastTeamLoader) {
1343	g.cacheMu.Lock()
1344	defer g.cacheMu.Unlock()
1345	g.fastTeamLoader = l
1346}
1347
1348func (g *GlobalContext) SetTeamAuditor(a TeamAuditor) {
1349	g.cacheMu.Lock()
1350	defer g.cacheMu.Unlock()
1351	g.teamAuditor = a
1352}
1353
1354func (g *GlobalContext) SetTeamBoxAuditor(a TeamBoxAuditor) {
1355	g.cacheMu.Lock()
1356	defer g.cacheMu.Unlock()
1357	g.teamBoxAuditor = a
1358}
1359
1360func (g *GlobalContext) SetStellar(s Stellar) {
1361	g.cacheMu.Lock()
1362	defer g.cacheMu.Unlock()
1363	g.stellar = s
1364}
1365
1366func (g *GlobalContext) SetDeviceEKStorage(s DeviceEKStorage) {
1367	g.cacheMu.Lock()
1368	defer g.cacheMu.Unlock()
1369	g.deviceEKStorage = s
1370}
1371
1372func (g *GlobalContext) SetUserEKBoxStorage(s UserEKBoxStorage) {
1373	g.cacheMu.Lock()
1374	defer g.cacheMu.Unlock()
1375	g.userEKBoxStorage = s
1376}
1377
1378func (g *GlobalContext) SetTeamEKBoxStorage(s TeamEKBoxStorage) {
1379	g.cacheMu.Lock()
1380	defer g.cacheMu.Unlock()
1381	g.teamEKBoxStorage = s
1382}
1383
1384func (g *GlobalContext) SetTeambotEKBoxStorage(s TeamEKBoxStorage) {
1385	g.cacheMu.Lock()
1386	defer g.cacheMu.Unlock()
1387	g.teambotEKBoxStorage = s
1388}
1389
1390func (g *GlobalContext) LoadUserByUID(uid keybase1.UID) (*User, error) {
1391	arg := NewLoadUserArgWithMetaContext(NewMetaContextBackground(g)).WithUID(uid).WithPublicKeyOptional()
1392	return LoadUser(arg)
1393}
1394
1395func (g *GlobalContext) BustLocalUserCache(ctx context.Context, u keybase1.UID) {
1396	g.GetUPAKLoader().Invalidate(ctx, u)
1397	_ = g.CardCache().Delete(u)
1398	_ = g.GetFullSelfer().HandleUserChanged(u)
1399}
1400
1401func (g *GlobalContext) OverrideUPAKLoader(upak UPAKLoader) {
1402	g.upakLoader = upak
1403}
1404
1405func (g *GlobalContext) AddUserChangedHandler(h UserChangedHandler) {
1406	g.uchMu.Lock()
1407	g.UserChangedHandlers = append(g.UserChangedHandlers, h)
1408	g.uchMu.Unlock()
1409}
1410
1411func (g *GlobalContext) GetOutOfDateInfo() keybase1.OutOfDateInfo {
1412	g.oodiMu.RLock()
1413	ret := *g.outOfDateInfo
1414	g.oodiMu.RUnlock()
1415	return ret
1416}
1417
1418func (g *GlobalContext) KeyfamilyChanged(ctx context.Context, u keybase1.UID) {
1419	g.Log.CDebugf(ctx, "+ KeyfamilyChanged(%s)", u)
1420	defer g.Log.CDebugf(ctx, "- KeyfamilyChanged(%s)", u)
1421
1422	// Make sure we kill the UPAK and full self cache for this user
1423	g.BustLocalUserCache(ctx, u)
1424
1425	if g.NotifyRouter != nil {
1426		g.NotifyRouter.HandleKeyfamilyChanged(u)
1427		// TODO: remove this when KBFS handles KeyfamilyChanged
1428		g.NotifyRouter.HandleUserChanged(NewMetaContext(ctx, g), u, "KeyfamilyChanged")
1429	}
1430}
1431
1432func (g *GlobalContext) UserChanged(ctx context.Context, u keybase1.UID) {
1433	g.Log.CDebugf(ctx, "+ UserChanged(%s)", u)
1434	defer g.Log.CDebugf(ctx, "- UserChanged(%s)", u)
1435
1436	_, _ = g.GetPerUserKeyring(context.TODO())
1437
1438	g.BustLocalUserCache(ctx, u)
1439	if g.NotifyRouter != nil {
1440		g.NotifyRouter.HandleUserChanged(NewMetaContext(ctx, g), u, "G.UserChanged")
1441	}
1442
1443	g.uchMu.Lock()
1444	list := g.UserChangedHandlers
1445	var newList []UserChangedHandler
1446	for _, cc := range list {
1447		if err := cc.HandleUserChanged(u); err == nil {
1448			newList = append(newList, cc)
1449		}
1450	}
1451	g.UserChangedHandlers = newList
1452	g.uchMu.Unlock()
1453}
1454
1455// GetPerUserKeyring recreates PerUserKeyring if the uid changes or this is none installed.
1456func (g *GlobalContext) GetPerUserKeyring(ctx context.Context) (ret *PerUserKeyring, err error) {
1457	defer g.Trace("G#GetPerUserKeyring", &err)()
1458
1459	myUID := g.ActiveDevice.UID()
1460	if myUID.IsNil() {
1461		return nil, errors.New("PerUserKeyring unavailable with no UID")
1462	}
1463
1464	// Don't do any operations under these locks that could come back and hit them again.
1465	// That's why GetMyUID up above is not under this lock.
1466	g.perUserKeyringMu.Lock()
1467	defer g.perUserKeyringMu.Unlock()
1468
1469	makeNew := func() (*PerUserKeyring, error) {
1470		pukring, err := NewPerUserKeyring(g, myUID)
1471		if err != nil {
1472			g.Log.CWarningf(ctx, "G#GetPerUserKeyring -> failed: %s", err)
1473			g.perUserKeyring = nil
1474			return nil, err
1475		}
1476		g.Log.CDebugf(ctx, "G#GetPerUserKeyring -> new")
1477		g.perUserKeyring = pukring
1478		return g.perUserKeyring, nil
1479	}
1480
1481	if g.perUserKeyring == nil {
1482		return makeNew()
1483	}
1484	pukUID := g.perUserKeyring.GetUID()
1485	if pukUID.Equal(myUID) {
1486		return g.perUserKeyring, nil
1487	}
1488	return makeNew()
1489}
1490
1491func (g *GlobalContext) ClearPerUserKeyring() {
1492	defer g.Trace("G#ClearPerUserKeyring", nil)()
1493
1494	g.perUserKeyringMu.Lock()
1495	defer g.perUserKeyringMu.Unlock()
1496	g.perUserKeyring = nil
1497}
1498
1499func (g *GlobalContext) LocalSigchainGuard() *LocalSigchainGuard {
1500	return g.localSigchainGuard
1501}
1502
1503func (g *GlobalContext) StartStandaloneChat() {
1504	if !g.Standalone {
1505		return
1506	}
1507
1508	if g.StandaloneChatConnector == nil {
1509		g.Log.Warning("G#StartStandaloneChat - not starting chat, StandaloneChatConnector is nil.")
1510		return
1511	}
1512
1513	_ = g.StandaloneChatConnector.StartStandaloneChat(g)
1514}
1515
1516func (g *GlobalContext) SecretStore() *SecretStoreLocked {
1517	g.secretStoreMu.Lock()
1518	defer g.secretStoreMu.Unlock()
1519
1520	return g.secretStore
1521}
1522
1523// ReplaceSecretStore gets the existing secret out of the existing
1524// secret store, creates a new secret store (could be a new type
1525// of SecretStore based on a config change), and inserts the secret
1526// into the new secret store.
1527func (g *GlobalContext) ReplaceSecretStore(ctx context.Context) error {
1528	g.secretStoreMu.Lock()
1529	defer g.secretStoreMu.Unlock()
1530
1531	username := g.Env.GetUsername()
1532	m := NewMetaContext(ctx, g)
1533
1534	// get the current secret
1535	secret, err := g.secretStore.RetrieveSecret(m, username)
1536	if err != nil {
1537		m.Debug("error retrieving existing secret for ReplaceSecretStore: %s", err)
1538		return err
1539	}
1540
1541	// clear the existing secret from the existing secret store
1542	if err := g.secretStore.ClearSecret(m, username); err != nil {
1543		m.Debug("error clearing existing secret for ReplaceSecretStore: %s", err)
1544		return err
1545	}
1546
1547	// make a new secret store
1548	g.secretStore = NewSecretStoreLocked(m)
1549
1550	// store the secret in the secret store
1551	if err := g.secretStore.StoreSecret(m, username, secret); err != nil {
1552		m.Debug("error storing existing secret for ReplaceSecretStore: %s", err)
1553		return err
1554	}
1555
1556	m.Debug("ReplaceSecretStore success")
1557
1558	return nil
1559}
1560
1561func (g *GlobalContext) IsOneshot(ctx context.Context) (bool, error) {
1562	uc, err := g.Env.GetConfig().GetUserConfig()
1563	if err != nil {
1564		g.Log.CDebugf(ctx, "IsOneshot: Error getting a user config: %s", err)
1565		return false, err
1566	}
1567	if uc == nil {
1568		g.Log.CDebugf(ctx, "IsOneshot: nil user config")
1569		return false, nil
1570	}
1571	return uc.IsOneshot(), nil
1572}
1573
1574func (g *GlobalContext) GetMeUV(ctx context.Context) (res keybase1.UserVersion, err error) {
1575	res = g.ActiveDevice.UserVersion()
1576	if res.IsNil() {
1577		return keybase1.UserVersion{}, LoginRequiredError{}
1578	}
1579	return res, nil
1580}
1581