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  }