github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/env/context.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package env 6 7 import ( 8 "errors" 9 "fmt" 10 "net" 11 "os" 12 "path/filepath" 13 "sync" 14 15 "github.com/keybase/client/go/kbconst" 16 "github.com/keybase/client/go/libkb" 17 "github.com/keybase/client/go/logger" 18 "github.com/keybase/client/go/protocol/keybase1" 19 "github.com/keybase/go-framed-msgpack-rpc/rpc" 20 ) 21 22 const ( 23 kbfsSocketFile = "kbfsd.sock" 24 ) 25 26 // AppStateUpdater is an interface for things that need to listen to 27 // app state changes. 28 type AppStateUpdater interface { 29 // NextAppStateUpdate returns a channel that app state changes 30 // are sent to. 31 NextAppStateUpdate(lastState *keybase1.MobileAppState) <-chan keybase1.MobileAppState 32 // NextNetworkStateUpdate returns a channel that mobile network 33 // state changes are sent to. 34 NextNetworkStateUpdate(lastState *keybase1.MobileNetworkState) <-chan keybase1.MobileNetworkState 35 } 36 37 // EmptyAppStateUpdater is an implementation of AppStateUpdater that 38 // never returns an update, for testing. 39 type EmptyAppStateUpdater struct{} 40 41 // NextAppStateUpdate implements AppStateUpdater. 42 func (easu EmptyAppStateUpdater) NextAppStateUpdate(lastState *keybase1.MobileAppState) <-chan keybase1.MobileAppState { 43 // Receiving on a nil channel blocks forever. 44 return nil 45 } 46 47 // NextNetworkStateUpdate implements AppStateUpdater. 48 func (easu EmptyAppStateUpdater) NextNetworkStateUpdate( 49 lastState *keybase1.MobileNetworkState) <-chan keybase1.MobileNetworkState { 50 // Receiving on a nil channel blocks forever. 51 return nil 52 } 53 54 // Context defines the environment for this package 55 type Context interface { 56 AppStateUpdater 57 GetRunMode() kbconst.RunMode 58 GetLogDir() string 59 GetDataDir() string 60 GetEnv() *libkb.Env 61 GetMountDir() (string, error) 62 ConfigureSocketInfo() (err error) 63 CheckService() error 64 GetSocket(clearError bool) (net.Conn, rpc.Transporter, bool, error) 65 NewRPCLogFactory() rpc.LogFactory 66 NewNetworkInstrumenter(keybase1.NetworkSource) rpc.NetworkInstrumenterStorage 67 GetKBFSSocket(clearError bool) (net.Conn, rpc.Transporter, bool, error) 68 BindToKBFSSocket() (net.Listener, error) 69 GetVDebugSetting() string 70 GetPerfLog() logger.Logger 71 } 72 73 // KBFSContext is an implementation for libkbfs.Context 74 type KBFSContext struct { 75 g *libkb.GlobalContext 76 // protects the socket primitives below 77 kbfsSocketMtx sync.RWMutex 78 kbfsSocket libkb.Socket 79 kbfsSocketWrapper *libkb.SocketWrapper 80 } 81 82 var _ Context = (*KBFSContext)(nil) 83 84 func (c *KBFSContext) initKBFSSocket() { 85 c.kbfsSocketMtx.Lock() 86 defer c.kbfsSocketMtx.Unlock() 87 log := c.g.Log 88 bindFile := c.getKBFSSocketFile() 89 dialFiles := []string{bindFile} 90 c.kbfsSocket = libkb.NewSocketWithFiles(log, bindFile, dialFiles) 91 } 92 93 // NewContextFromGlobalContext constructs a context 94 func NewContextFromGlobalContext(g *libkb.GlobalContext) *KBFSContext { 95 c := &KBFSContext{g: g} 96 c.initKBFSSocket() 97 return c 98 } 99 100 func newContextFromG(g *libkb.GlobalContext) *KBFSContext { 101 err := g.ConfigureConfig() 102 if err != nil { 103 panic(err) 104 } 105 err = g.ConfigureLogging(nil) 106 if err != nil { 107 panic(err) 108 } 109 err = g.ConfigureCaches() 110 if err != nil { 111 panic(err) 112 } 113 err = g.ConfigureMerkleClient() 114 if err != nil { 115 panic(err) 116 } 117 return NewContextFromGlobalContext(g) 118 } 119 120 // NewContext constructs a context. This should only be called once in 121 // main functions. 122 func NewContext() *KBFSContext { 123 g := libkb.NewGlobalContextInit() 124 return newContextFromG(g) 125 } 126 127 // NewContextWithPerfLog constructs a context with a specific perf 128 // log. This should only be called once in main functions. 129 func NewContextWithPerfLog(logName string) *KBFSContext { 130 g := libkb.NewGlobalContextInit() 131 132 // Override the perf file for this process, before logging is 133 // initialized. 134 if os.Getenv("KEYBASE_PERF_LOG_FILE") == "" { 135 os.Setenv("KEYBASE_PERF_LOG_FILE", filepath.Join( 136 g.Env.GetLogDir(), logName)) 137 } 138 139 return newContextFromG(g) 140 } 141 142 // GetLogDir returns log dir 143 func (c *KBFSContext) GetLogDir() string { 144 return c.g.Env.GetLogDir() 145 } 146 147 // GetDataDir returns log dir 148 func (c *KBFSContext) GetDataDir() string { 149 return c.g.Env.GetDataDir() 150 } 151 152 // GetMountDir returns mount dir 153 func (c *KBFSContext) GetMountDir() (string, error) { 154 return c.g.Env.GetMountDir() 155 } 156 157 // GetEnv returns the global Env 158 func (c *KBFSContext) GetEnv() *libkb.Env { 159 return c.g.Env 160 } 161 162 // GetRunMode returns run mode 163 func (c *KBFSContext) GetRunMode() kbconst.RunMode { 164 return c.g.GetRunMode() 165 } 166 167 // GetPerfLog returns the perf log. 168 func (c *KBFSContext) GetPerfLog() logger.Logger { 169 return c.g.GetPerfLog() 170 } 171 172 // NextAppStateUpdate implements AppStateUpdater. 173 func (c *KBFSContext) NextAppStateUpdate(lastState *keybase1.MobileAppState) <-chan keybase1.MobileAppState { 174 if c.g.MobileAppState == nil { 175 return nil 176 } 177 return c.g.MobileAppState.NextUpdate(lastState) 178 } 179 180 // NextNetworkStateUpdate implements AppStateUpdater. 181 func (c *KBFSContext) NextNetworkStateUpdate( 182 lastState *keybase1.MobileNetworkState) <-chan keybase1.MobileNetworkState { 183 if c.g.MobileNetState == nil { 184 return nil 185 } 186 return c.g.MobileNetState.NextUpdate(lastState) 187 } 188 189 // CheckService checks if the service is running and returns nil if 190 // so, and an error otherwise. 191 func (c *KBFSContext) CheckService() error { 192 // Trying to dial the service seems like the best 193 // platform-agnostic way of seeing if the service is up. 194 // Stat-ing the socket file, for example, doesn't work for 195 // Windows named pipes. 196 s, err := libkb.NewSocket(c.g) 197 if err != nil { 198 return err 199 } 200 conn, err := s.DialSocket() 201 if err != nil { 202 switch libkb.RuntimeGroup() { 203 case keybase1.RuntimeGroup_DARWINLIKE, keybase1.RuntimeGroup_WINDOWSLIKE: 204 return errors.New( 205 "keybase isn't running; open the Keybase app") 206 default: 207 return errors.New( 208 "keybase isn't running; try `run_keybase`") 209 } 210 } 211 err = conn.Close() 212 if err != nil { 213 return err 214 } 215 return nil 216 } 217 218 // GetSocket returns a socket 219 func (c *KBFSContext) GetSocket(clearError bool) ( 220 net.Conn, rpc.Transporter, bool, error) { 221 return c.g.GetSocket(clearError) 222 } 223 224 // ConfigureSocketInfo configures a socket 225 func (c *KBFSContext) ConfigureSocketInfo() error { 226 return c.g.ConfigureSocketInfo() 227 } 228 229 // NewRPCLogFactory constructs an RPC logger 230 func (c *KBFSContext) NewRPCLogFactory() rpc.LogFactory { 231 return &libkb.RPCLogFactory{Contextified: libkb.NewContextified(c.g)} 232 } 233 234 // NewNetworkInstrumenter constructs an RPC NetworkInstrumenterStorage 235 func (c *KBFSContext) NewNetworkInstrumenter(src keybase1.NetworkSource) rpc.NetworkInstrumenterStorage { 236 return libkb.NetworkInstrumenterStorageFromSrc(c.g, src) 237 } 238 239 func (c *KBFSContext) getSandboxSocketFile() string { 240 sandboxDir := c.g.Env.HomeFinder.SandboxCacheDir() 241 if sandboxDir == "" { 242 return "" 243 } 244 return filepath.Join(sandboxDir, kbfsSocketFile) 245 } 246 247 func (c *KBFSContext) getKBFSSocketFile() string { 248 e := c.g.Env 249 return e.GetString( 250 c.getSandboxSocketFile, 251 // TODO: maybe add command-line option here 252 func() string { return os.Getenv("KBFS_SOCKET_FILE") }, 253 func() string { return filepath.Join(e.GetRuntimeDir(), kbfsSocketFile) }, 254 ) 255 } 256 257 func (c *KBFSContext) newTransportFromSocket(s net.Conn) rpc.Transporter { 258 return rpc.NewTransport(s, c.NewRPCLogFactory(), c.NewNetworkInstrumenter(keybase1.NetworkSource_LOCAL), 259 libkb.WrapError, rpc.DefaultMaxFrameLength) 260 } 261 262 // GetKBFSSocket dials the socket configured in `c.kbfsSocket`. 263 // Adapted from github.com/keybase/client/go/libkb.GlobalContext.GetSocket. 264 func (c *KBFSContext) GetKBFSSocket(clearError bool) ( 265 net.Conn, rpc.Transporter, bool, error) { 266 var err error 267 c.g.Trace("GetSocket", &err)() 268 269 // Protect all global socket wrapper manipulation with a 270 // lock to prevent race conditions. 271 c.kbfsSocketMtx.Lock() 272 defer c.kbfsSocketMtx.Unlock() 273 274 needWrapper := false 275 if c.kbfsSocketWrapper == nil { 276 needWrapper = true 277 c.g.Log.Debug("empty socket wrapper; need a new one") 278 } else if c.kbfsSocketWrapper.Transporter != nil && !c.kbfsSocketWrapper.Transporter.IsConnected() { 279 // need reconnect 280 c.g.Log.Debug("rpc transport isn't connected, reconnecting...") 281 needWrapper = true 282 } 283 284 isNew := false 285 if needWrapper { 286 sw := libkb.SocketWrapper{} 287 if c.kbfsSocket == nil { 288 sw.Err = fmt.Errorf("Cannot get socket") 289 } else { 290 sw.Conn, sw.Err = c.kbfsSocket.DialSocket() 291 c.g.Log.Debug("DialSocket -> %s", libkb.ErrToOk(sw.Err)) 292 isNew = true 293 } 294 if sw.Err == nil { 295 sw.Transporter = c.newTransportFromSocket(sw.Conn) 296 } 297 c.kbfsSocketWrapper = &sw 298 } 299 300 // Return the current error no matter what 301 sw := c.kbfsSocketWrapper 302 if sw.Err != nil && clearError { 303 c.kbfsSocketWrapper = nil 304 } 305 306 return sw.Conn, sw.Transporter, isNew, sw.Err 307 } 308 309 // cleanupSocketFile cleans up the socket file for binding. 310 func (c *KBFSContext) cleanupSocketFile() error { 311 switch sock := c.kbfsSocket.(type) { 312 case libkb.SocketInfo: 313 sf := sock.GetBindFile() 314 if exists, err := libkb.FileExists(sf); err != nil { 315 return err 316 } else if exists { 317 c.g.Log.Debug("removing stale socket file: %s", sf) 318 if err = os.Remove(sf); err != nil { 319 c.g.Log.Warning("error removing stale socket file: %s", err) 320 return err 321 } 322 } 323 case nil: 324 return errors.New("socket not initialized") 325 default: 326 return errors.New("invalid socket type") 327 } 328 return nil 329 } 330 331 // BindToKBFSSocket binds to the socket configured in `c.kbfsSocket`. 332 func (c *KBFSContext) BindToKBFSSocket() (net.Listener, error) { 333 c.kbfsSocketMtx.Lock() 334 defer c.kbfsSocketMtx.Unlock() 335 err := c.cleanupSocketFile() 336 if err != nil { 337 return nil, err 338 } 339 return c.kbfsSocket.BindToSocket() 340 } 341 342 // GetVDebugSetting returns the verbose debug logger. 343 func (c *KBFSContext) GetVDebugSetting() string { 344 return c.g.Env.GetVDebugSetting() 345 }