/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/libkb/socket_nix.go
172 строки
5 KB
chrisnojima-zoom
Version 670 - clean2 (#29122)
08 июн 2026, 19:31
Не верифицирован
08 июн 2026, 19:31
1943197
Код
Авторство
О чём код?
// Copyright 2015 Keybase, Inc. All rights reserved. Use of // this source code is governed by the included BSD license. //go:build !windows // socket_nix.go package libkb import ( "fmt" "net" "os" "path/filepath" "strings" "sync" "github.com/keybase/client/go/logger" ) // Though I've never seen this come up in production, it definitely comes up // in systests that multiple go-routines might race over the current // working directory as they do the (chdir && dial) dance below. Make sure // a lock is held whenever operating on sockets, so that two racing goroutines // can't conflict here. var bindLock sync.Mutex func (s SocketInfo) BindToSocket() (ret net.Listener, err error) { // Lock so that multiple goroutines can't race over current working dir. // See note above. bindLock.Lock() defer bindLock.Unlock() bindFile := s.bindFile what := fmt.Sprintf("SocketInfo#BindToSocket(unix:%s)", bindFile) defer Trace(s.log, what, &err)() if err := MakeParentDirs(s.log, bindFile); err != nil { return nil, err } // Path can't be longer than N characters. // In this case Chdir to the file directory first and use a local path. // On many Linuxes, N=108, on some N=106, and on macOS N=104. // N=104 is the lowest I know of. // It's the length of the path buffer in sockaddr_un. // And there may be a null-terminator in there, not sure, so make it 103 for good luck. // https://github.com/golang/go/issues/6895#issuecomment-98006662 // https://gist.github.com/mlsteele/16dc5b6eb3d112b914183928c9af71b8#file-un-h-L79 // We could always Chdir, but then this function would be non-threadsafe more of the time. // Pick your poison. if len(bindFile) >= 103 { prevWd, err := os.Getwd() if err != nil { return nil, fmt.Errorf("Error getting working directory: %s", err) } s.log.Debug("| Changing current working directory because path for binding is too long") dir := filepath.Dir(bindFile) s.log.Debug("| Chdir(%s)", dir) if err := os.Chdir(dir); err != nil { return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err) } defer func() { s.log.Debug("| Chdir(%s)", prevWd) err := os.Chdir(prevWd) if err != nil { s.log.Debug("| Chdir(%s) err=%v", prevWd, err) } }() bindFile = filepath.Base(bindFile) } s.log.Info("| net.Listen on unix:%s", bindFile) ret, err = net.Listen("unix", bindFile) if err != nil { s.log.Warning("net.Listen failed with: %s", err.Error()) } return ret, err } func (s SocketInfo) DialSocket() (net.Conn, error) { errs := []error{} for _, file := range s.dialFiles { ret, err := s.dialSocket(file) if err == nil { return ret, nil } errs = append(errs, err) } return nil, CombineErrors(errs...) } func (s SocketInfo) dialSocket(dialFile string) (ret net.Conn, err error) { // Lock so that multiple goroutines can't race over current working dir. // See note above. bindLock.Lock() defer bindLock.Unlock() what := fmt.Sprintf("SocketInfo#dialSocket(unix:%s)", dialFile) defer Trace(s.log, what, &err)() if dialFile == "" { return nil, fmt.Errorf("Can't dial empty path") } // Path can't be longer than 103 characters. // In this case Chdir to the file directory first. // https://github.com/golang/go/issues/6895#issuecomment-98006662 if len(dialFile) >= 103 { prevWd, err := os.Getwd() if err != nil { return nil, fmt.Errorf("Error getting working directory: %s", err) } s.log.Debug("| Changing current working directory because path for dialing is too long") dir := filepath.Dir(dialFile) s.log.Debug("| os.Chdir(%s)", dir) if err := os.Chdir(dir); err != nil { return nil, fmt.Errorf("Path can't be longer than 108 characters (failed to chdir): %s", err) } defer func() { err := os.Chdir(prevWd) if err != nil { s.log.Debug("| Chdir(%s) err=%v", prevWd, err) } }() dialFile = filepath.Base(dialFile) } s.log.Debug("| net.Dial(unix:%s)", dialFile) return net.Dial("unix", dialFile) } func NewSocket(g *GlobalContext) (ret Socket, err error) { var dialFiles []string dialFiles, err = g.Env.GetSocketDialFiles() if err != nil { return } var bindFile string bindFile, err = g.Env.GetSocketBindFile() if err != nil { return } g.Log.Debug("Connecting to socket with dialFiles=%s, bindFiles=%s", dialFiles, bindFile) log := g.Log if log == nil { log = logger.NewNull() } ret = SocketInfo{ log: log, dialFiles: dialFiles, bindFile: bindFile, } return } func NewSocketWithFiles( log logger.Logger, bindFile string, dialFiles []string, ) Socket { return SocketInfo{ log: log, bindFile: bindFile, dialFiles: dialFiles, } } // net.errClosing isn't exported, so do this.. UGLY! func IsSocketClosedError(e error) bool { return strings.HasSuffix(e.Error(), "use of closed network connection") }