From a6df2be92ea15a90a4fb7589060fda04c92dfe6e Mon Sep 17 00:00:00 2001 From: Siddarth Kumar Date: Thu, 14 Dec 2023 19:02:38 +0530 Subject: [PATCH] fix crash on x86_64 android emulators (#4233) Due to presence of syscall to `epoll_wait` android emulators after Android 10 would crash. This commit has added `amd64` fallback implementations in 2 spots - `tcp-shaker` ref : https://github.com/status-im/tcp-shaker/commit/571aa46f35a5bb72b8bc8306fad2f87bb75def12 - `notify` ref : https://github.com/status-im/notify/commit/0eb36e6c1a02a5d8dd3c663a04dcb46ddfb49593 The build flags are updated for fallback implementation to only be used in case of `x86_64` simulators. status-mobile PR : https://github.com/status-im/status-mobile/pull/17773 --- go.mod | 4 +- go.sum | 10 +- protocol/messenger_mailserver_cycle.go | 152 ++++--- vendor/github.com/rjeczalik/notify/README.md | 2 +- .../rjeczalik/notify/watcher_inotify.go | 3 +- .../rjeczalik/notify/watcher_inotify_amd64.go | 418 ++++++++++++++++++ .../status-im/tcp-shaker/socket_linux.go | 2 + .../status-im/tcp-shaker/socket_linux_amd.go | 132 ++++++ vendor/modules.txt | 5 +- 9 files changed, 657 insertions(+), 71 deletions(-) create mode 100644 vendor/github.com/rjeczalik/notify/watcher_inotify_amd64.go create mode 100644 vendor/github.com/status-im/tcp-shaker/socket_linux_amd.go diff --git a/go.mod b/go.mod index 1e70b9be7..187f9352e 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.19 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.11 +replace github.com/rjeczalik/notify => github.com/status-im/notify v1.0.2-status + replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1 replace github.com/nfnt/resize => github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088 @@ -51,7 +53,7 @@ require ( github.com/status-im/migrate/v4 v4.6.2-status.3 github.com/status-im/rendezvous v1.3.7 github.com/status-im/status-go/extkeys v1.1.2 - github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501 + github.com/status-im/tcp-shaker v1.1.1-status github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857 github.com/stretchr/testify v1.8.4 github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a diff --git a/go.sum b/go.sum index f44ff44dc..e78349242 100644 --- a/go.sum +++ b/go.sum @@ -1855,10 +1855,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= -github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= -github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= -github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -1998,14 +1994,16 @@ github.com/status-im/markdown v0.0.0-20231114210825-6c2d15b5dc57/go.mod h1:5rjPy github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8= github.com/status-im/migrate/v4 v4.6.2-status.3 h1:Khwjb59NzniloUr5i9s9AtkEyqBbQFt1lkoAu66sAu0= github.com/status-im/migrate/v4 v4.6.2-status.3/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8= +github.com/status-im/notify v1.0.2-status h1:x8wev0Sh8H8KAf4bVcv+L0dVHldBESOKUlqRqRY7uL8= +github.com/status-im/notify v1.0.2-status/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= github.com/status-im/rendezvous v1.3.7 h1:rZGWsFCjPV3MWeUkLkZSOGTAvyRf+rxx5hnEGLE4OHg= github.com/status-im/rendezvous v1.3.7/go.mod h1:r0vCbQJByTteMajN0f+Mcet/Vd7uAXxFPfewNpI2iXQ= github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088 h1:ClCAP2FPCvl8hGMhbUx/tq/sOu2wibztAa5jAvQEe4Q= github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088/go.mod h1:+92j1tN27DypDeBFxkg0uzkqfh1bNHTZe3Bv2PjvxpM= github.com/status-im/status-go/extkeys v1.1.2 h1:FSjARgDathJ3rIapJt851LsIXP9Oyuu2M2jPJKuzloU= github.com/status-im/status-go/extkeys v1.1.2/go.mod h1:hCmFzb2jiiVF2voZKYbzuhOQiHHCmyLJsZJXrFFg7BY= -github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501 h1:oa0KU5jJRNtXaM/P465MhvSFo/HM2O8qi2DDuPcd7ro= -github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501/go.mod h1:RYo/itke1oU5k/6sj9DNM3QAwtE5rZSgg5JnkOv83hk= +github.com/status-im/tcp-shaker v1.1.1-status h1:TnVeeWlq2SKCWotHc4Vi6qZQfY8TTe3VLmu1xpEFYhg= +github.com/status-im/tcp-shaker v1.1.1-status/go.mod h1:RYo/itke1oU5k/6sj9DNM3QAwtE5rZSgg5JnkOv83hk= github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857 h1:sPkzT7Z7uLmejOsBRlZ0kwDWpqjpHJsp834o5nbhqho= github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857/go.mod h1:lq9I5ROto5tcua65GmCE6SIW7VE0ucdEBs1fn4z7uWU= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= diff --git a/protocol/messenger_mailserver_cycle.go b/protocol/messenger_mailserver_cycle.go index b599759f7..9f00e287d 100644 --- a/protocol/messenger_mailserver_cycle.go +++ b/protocol/messenger_mailserver_cycle.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "math/big" + "runtime" "sort" "strings" "time" @@ -22,6 +23,8 @@ import ( const defaultBackoff = 10 * time.Second const graylistBackoff = 3 * time.Minute +const isAndroidEmulator = runtime.GOOS == "android" && runtime.GOARCH == "amd64" +const findNearestMailServer = !isAndroidEmulator func (m *Messenger) mailserversByFleet(fleet string) []mailservers.Mailserver { var items []mailservers.Mailserver @@ -235,76 +238,104 @@ func (m *Messenger) findNewMailserver() error { allMailservers := m.mailserverCycle.allMailservers - m.logger.Info("Finding a new mailserver...") + // TODO: remove this check once sockets are stable on x86_64 emulators + if findNearestMailServer { + m.logger.Info("Finding a new mailserver...") - var mailserverStr []string - for _, m := range allMailservers { - mailserverStr = append(mailserverStr, m.Address) - } - - if len(allMailservers) == 0 { - m.logger.Warn("no mailservers available") // Do nothing... - return nil - - } - - var parseFn func(string) (string, error) - if allMailservers[0].Version == 2 { - parseFn = mailservers.MultiAddressToAddress - } else { - parseFn = mailservers.EnodeStringToAddr - } - - pingResult, err := mailservers.DoPing(context.Background(), mailserverStr, 500, parseFn) - if err != nil { - return err - } - - var availableMailservers []*mailservers.PingResult - for _, result := range pingResult { - if result.Err != nil { - m.logger.Info("connecting error", zap.String("err", *result.Err)) - continue // The results with error are ignored + var mailserverStr []string + for _, m := range allMailservers { + mailserverStr = append(mailserverStr, m.Address) } - availableMailservers = append(availableMailservers, result) - } - if len(availableMailservers) == 0 { - m.logger.Warn("No mailservers available") // Do nothing... - return nil + if len(allMailservers) == 0 { + m.logger.Warn("no mailservers available") // Do nothing... + return nil + + } + + var parseFn func(string) (string, error) + if allMailservers[0].Version == 2 { + parseFn = mailservers.MultiAddressToAddress + } else { + parseFn = mailservers.EnodeStringToAddr + } + + pingResult, err := mailservers.DoPing(context.Background(), mailserverStr, 500, parseFn) + if err != nil { + // pinging mailservers might fail, but we don't care + m.logger.Warn("mailservers.DoPing failed with", zap.Error(err)) + } + + var availableMailservers []*mailservers.PingResult + for _, result := range pingResult { + if result.Err != nil { + m.logger.Info("connecting error", zap.String("err", *result.Err)) + continue // The results with error are ignored + } + availableMailservers = append(availableMailservers, result) + } + + if len(availableMailservers) == 0 { + m.logger.Warn("No mailservers available") // Do nothing... + return nil + } + + mailserversByAddress := make(map[string]mailservers.Mailserver) + for idx := range allMailservers { + mailserversByAddress[allMailservers[idx].Address] = allMailservers[idx] + } + var sortedMailservers []SortedMailserver + for _, ping := range availableMailservers { + address := ping.Address + ms := mailserversByAddress[address] + sortedMailserver := SortedMailserver{ + Address: address, + RTTMs: *ping.RTTMs, + } + m.mailPeersMutex.Lock() + pInfo, ok := m.mailserverCycle.peers[ms.ID] + m.mailPeersMutex.Unlock() + if ok { + if time.Now().Before(pInfo.canConnectAfter) { + continue // We can't connect to this node yet + } + } + + sortedMailservers = append(sortedMailservers, sortedMailserver) + + } + sort.Sort(byRTTMsAndCanConnectBefore(sortedMailservers)) + + // Picks a random mailserver amongs the ones with the lowest latency + // The pool size is 1/4 of the mailservers were pinged successfully + pSize := poolSize(len(sortedMailservers) - 1) + if pSize <= 0 { + pSize = len(sortedMailservers) + if pSize <= 0 { + m.logger.Warn("No mailservers available") // Do nothing... + return nil + } + } + + r, err := rand.Int(rand.Reader, big.NewInt(int64(pSize))) + if err != nil { + return err + } + + msPing := sortedMailservers[r.Int64()] + ms := mailserversByAddress[msPing.Address] + m.logger.Info("connecting to mailserver", zap.String("address", ms.Address)) + return m.connectToMailserver(ms) } mailserversByAddress := make(map[string]mailservers.Mailserver) for idx := range allMailservers { mailserversByAddress[allMailservers[idx].Address] = allMailservers[idx] } - var sortedMailservers []SortedMailserver - for _, ping := range availableMailservers { - address := ping.Address - ms := mailserversByAddress[address] - sortedMailserver := SortedMailserver{ - Address: address, - RTTMs: *ping.RTTMs, - } - m.mailPeersMutex.Lock() - pInfo, ok := m.mailserverCycle.peers[ms.ID] - m.mailPeersMutex.Unlock() - if ok { - if time.Now().Before(pInfo.canConnectAfter) { - continue // We can't connect to this node yet - } - } - sortedMailservers = append(sortedMailservers, sortedMailserver) - - } - sort.Sort(byRTTMsAndCanConnectBefore(sortedMailservers)) - - // Picks a random mailserver amongs the ones with the lowest latency - // The pool size is 1/4 of the mailservers were pinged successfully - pSize := poolSize(len(sortedMailservers) - 1) + pSize := poolSize(len(allMailservers) - 1) if pSize <= 0 { - pSize = len(sortedMailservers) + pSize = len(allMailservers) if pSize <= 0 { m.logger.Warn("No mailservers available") // Do nothing... return nil @@ -316,10 +347,11 @@ func (m *Messenger) findNewMailserver() error { return err } - msPing := sortedMailservers[r.Int64()] + msPing := allMailservers[r.Int64()] ms := mailserversByAddress[msPing.Address] m.logger.Info("connecting to mailserver", zap.String("address", ms.Address)) return m.connectToMailserver(ms) + } func (m *Messenger) activeMailserverStatus() (connStatus, error) { diff --git a/vendor/github.com/rjeczalik/notify/README.md b/vendor/github.com/rjeczalik/notify/README.md index 0ff880e36..8216cb189 100644 --- a/vendor/github.com/rjeczalik/notify/README.md +++ b/vendor/github.com/rjeczalik/notify/README.md @@ -1,7 +1,7 @@ notify [![GoDoc](https://godoc.org/github.com/rjeczalik/notify?status.svg)](https://godoc.org/github.com/rjeczalik/notify) [![Build Status](https://img.shields.io/travis/rjeczalik/notify/master.svg)](https://travis-ci.org/rjeczalik/notify "inotify + FSEvents + kqueue") [![Build status](https://img.shields.io/appveyor/ci/rjeczalik/notify-246.svg)](https://ci.appveyor.com/project/rjeczalik/notify-246 "ReadDirectoryChangesW") [![Coverage Status](https://img.shields.io/coveralls/rjeczalik/notify/master.svg)](https://coveralls.io/r/rjeczalik/notify?branch=master) ====== -Filesystem event notification library on steroids. (under active development) +Filesystem event notification library on steroids. *Documentation* diff --git a/vendor/github.com/rjeczalik/notify/watcher_inotify.go b/vendor/github.com/rjeczalik/notify/watcher_inotify.go index 02b432844..f3e2b1134 100644 --- a/vendor/github.com/rjeczalik/notify/watcher_inotify.go +++ b/vendor/github.com/rjeczalik/notify/watcher_inotify.go @@ -2,8 +2,9 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -//go:build linux +//go:build linux && !(android && amd64) // +build linux +// +build !android !amd64 package notify diff --git a/vendor/github.com/rjeczalik/notify/watcher_inotify_amd64.go b/vendor/github.com/rjeczalik/notify/watcher_inotify_amd64.go new file mode 100644 index 000000000..c86cd35dc --- /dev/null +++ b/vendor/github.com/rjeczalik/notify/watcher_inotify_amd64.go @@ -0,0 +1,418 @@ +// Copyright (c) 2014-2015 The Notify Authors. All rights reserved. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +//go:build android && amd64 +// +build android,amd64 + +package notify + +import ( + "bytes" + "errors" + "golang.org/x/sys/unix" + "path/filepath" + _ "path/filepath" + "runtime" + "sync" + "sync/atomic" + _ "syscall" + "unsafe" +) + +// eventBufferSize defines the size of the buffer given to read(2) function. One +// should not depend on this value, since it was arbitrary chosen and may be +// changed in the future. +const eventBufferSize = 64 * (unix.SizeofInotifyEvent + unix.PathMax + 1) + +// consumersCount defines the number of consumers in producer-consumer based +// implementation. Each consumer is run in a separate goroutine and has read +// access to watched files map. +const consumersCount = 2 + +const invalidDescriptor = -1 + +// watched is a pair of file path and inotify mask used as a value in +// watched files map. +type watched struct { + path string + mask uint32 +} + +// inotify implements Watcher interface. +type inotify struct { + sync.RWMutex // protects inotify.m map + m map[int32]*watched // watch descriptor to watched object + fd int32 // inotify file descriptor + pipefd []int // pipe's read and write descriptors + epfd int // epoll descriptor + epes []unix.EpollEvent // epoll events + buffer [eventBufferSize]byte // inotify event buffer + wg sync.WaitGroup // wait group used to close main loop + c chan<- EventInfo // event dispatcher channel +} + +// NewWatcher creates new non-recursive inotify backed by inotify. +func newWatcher(c chan<- EventInfo) watcher { + i := &inotify{ + m: make(map[int32]*watched), + fd: invalidDescriptor, + pipefd: []int{invalidDescriptor, invalidDescriptor}, + epfd: invalidDescriptor, + epes: make([]unix.EpollEvent, 0), + c: c, + } + runtime.SetFinalizer(i, func(i *inotify) { + i.epollclose() + if i.fd != invalidDescriptor { + unix.Close(int(i.fd)) + } + }) + return i +} + +// Watch implements notify.watcher interface. +func (i *inotify) Watch(path string, e Event) error { + return i.watch(path, e) +} + +// Rewatch implements notify.watcher interface. +func (i *inotify) Rewatch(path string, _, newevent Event) error { + return i.watch(path, newevent) +} + +// watch adds a new watcher to the set of watched objects or modifies the existing +// one. If called for the first time, this function initializes inotify filesystem +// monitor and starts producer-consumers goroutines. +func (i *inotify) watch(path string, e Event) (err error) { + if e&^(All|Event(unix.IN_ALL_EVENTS)) != 0 { + return errors.New("notify: unknown event") + } + if err = i.lazyinit(); err != nil { + return + } + iwd, err := unix.InotifyAddWatch(int(i.fd), path, encode(e)) + if err != nil { + return + } + i.Lock() + if wd, ok := i.m[int32(iwd)]; !ok { + i.m[int32(iwd)] = &watched{path: path, mask: uint32(e)} + } else { + wd.path = path + wd.mask = uint32(e) + } + i.Unlock() + return nil +} + +// lazyinit sets up all required file descriptors and starts 1+consumersCount +// goroutines. The producer goroutine blocks until file-system notifications +// occur. Then, all events are read from system buffer and sent to consumer +// goroutines which construct valid notify events. This method uses +// Double-Checked Locking optimization. +func (i *inotify) lazyinit() error { + if atomic.LoadInt32(&i.fd) == invalidDescriptor { + i.Lock() + defer i.Unlock() + if atomic.LoadInt32(&i.fd) == invalidDescriptor { + fd, err := unix.InotifyInit1(unix.IN_CLOEXEC) + if err != nil { + return err + } + i.fd = int32(fd) + if err = i.epollinit(); err != nil { + _, _ = i.epollclose(), unix.Close(int(fd)) // Ignore errors. + i.fd = invalidDescriptor + return err + } + esch := make(chan []*event) + go i.loop(esch) + i.wg.Add(consumersCount) + for n := 0; n < consumersCount; n++ { + go i.send(esch) + } + } + } + return nil +} + +// epollinit opens an epoll file descriptor and creates a pipe which will be +// used to wake up the epoll_wait(2) function. Then, file descriptor associated +// with inotify event queue and the read end of the pipe are added to epoll set. +// Note that `fd` member must be set before this function is called. +func (i *inotify) epollinit() (err error) { + if i.epfd, err = unix.EpollCreate1(0); err != nil { + return + } + if err = unix.Pipe(i.pipefd); err != nil { + return + } + i.epes = []unix.EpollEvent{ + {Events: unix.EPOLLIN, Fd: i.fd}, + {Events: unix.EPOLLIN, Fd: int32(i.pipefd[0])}, + } + if err = unix.EpollCtl(i.epfd, unix.EPOLL_CTL_ADD, int(i.fd), &i.epes[0]); err != nil { + return + } + return unix.EpollCtl(i.epfd, unix.EPOLL_CTL_ADD, i.pipefd[0], &i.epes[1]) +} + +// epollclose closes the file descriptor created by the call to epoll_create(2) +// and two file descriptors opened by pipe(2) function. +func (i *inotify) epollclose() (err error) { + if i.epfd != invalidDescriptor { + if err = unix.Close(i.epfd); err == nil { + i.epfd = invalidDescriptor + } + } + for n, fd := range i.pipefd { + if fd != invalidDescriptor { + switch e := unix.Close(fd); { + case e != nil && err == nil: + err = e + case e == nil: + i.pipefd[n] = invalidDescriptor + } + } + } + return +} + +func (i *inotify) loop(esch chan<- []*event) { + inotifyCh := make(chan []*event) + closeCh := make(chan struct{}) + + go func() { + for { + events := i.read() + if events != nil { + inotifyCh <- events + } + } + }() + + go func() { + buf := make([]byte, 1) + for { + _, err := unix.Read(i.pipefd[0], buf) + if err != nil { + closeCh <- struct{}{} + return + } + } + }() + + for { + select { + case events := <-inotifyCh: + esch <- events + case <-closeCh: + i.closeResources(esch) + return + } + } +} + +func (i *inotify) closeResources(esch chan<- []*event) { + i.Lock() + defer i.Unlock() + + fd := atomic.LoadInt32(&i.fd) + if err := unix.Close(int(fd)); err != nil && err != unix.EINTR { + panic("notify: close(2) error " + err.Error()) + } + + atomic.StoreInt32(&i.fd, invalidDescriptor) + if err := i.epollclose(); err != nil && err != unix.EINTR { + panic("notify: epollclose error " + err.Error()) + } + + close(esch) +} + +// read reads events from an inotify file descriptor. It does not handle errors +// returned from read(2) function since they are not critical to watcher logic. +func (i *inotify) read() (es []*event) { + n, err := unix.Read(int(i.fd), i.buffer[:]) + if err != nil || n < unix.SizeofInotifyEvent { + return + } + var sys *unix.InotifyEvent + nmin := n - unix.SizeofInotifyEvent + for pos, path := 0, ""; pos <= nmin; { + sys = (*unix.InotifyEvent)(unsafe.Pointer(&i.buffer[pos])) + pos += unix.SizeofInotifyEvent + if path = ""; sys.Len > 0 { + endpos := pos + int(sys.Len) + path = string(bytes.TrimRight(i.buffer[pos:endpos], "\x00")) + pos = endpos + } + es = append(es, &event{ + sys: unix.InotifyEvent{ + Wd: sys.Wd, + Mask: sys.Mask, + Cookie: sys.Cookie, + }, + path: path, + }) + } + return +} + +// send is a consumer function which sends events to event dispatcher channel. +// It is run in a separate goroutine in order to not block loop method when +// possibly expensive write operations are performed on inotify map. +func (i *inotify) send(esch <-chan []*event) { + for es := range esch { + for _, e := range i.transform(es) { + if e != nil { + i.c <- e + } + } + } + i.wg.Done() +} + +// transform prepares events read from inotify file descriptor for sending to +// user. It removes invalid events and these which are no longer present in +// inotify map. This method may also split one raw event into two different ones +// when system-dependent result is required. +func (i *inotify) transform(es []*event) []*event { + var multi []*event + i.RLock() + for idx, e := range es { + if e.sys.Mask&(unix.IN_IGNORED|unix.IN_Q_OVERFLOW) != 0 { + es[idx] = nil + continue + } + wd, ok := i.m[e.sys.Wd] + if !ok || e.sys.Mask&encode(Event(wd.mask)) == 0 { + es[idx] = nil + continue + } + if e.path == "" { + e.path = wd.path + } else { + e.path = filepath.Join(wd.path, e.path) + } + multi = append(multi, decode(Event(wd.mask), e)) + if e.event == 0 { + es[idx] = nil + } + } + i.RUnlock() + es = append(es, multi...) + return es +} + +// encode converts notify system-independent events to valid inotify mask +// which can be passed to inotify_add_watch(2) function. +func encode(e Event) uint32 { + if e&Create != 0 { + e = (e ^ Create) | InCreate | InMovedTo + } + if e&Remove != 0 { + e = (e ^ Remove) | InDelete | InDeleteSelf + } + if e&Write != 0 { + e = (e ^ Write) | InModify + } + if e&Rename != 0 { + e = (e ^ Rename) | InMovedFrom | InMoveSelf + } + return uint32(e) +} + +// decode uses internally stored mask to distinguish whether system-independent +// or system-dependent event is requested. The first one is created by modifying +// `e` argument. decode method sets e.event value to 0 when an event should be +// skipped. System-dependent event is set as the function's return value which +// can be nil when the event should not be passed on. +func decode(mask Event, e *event) (syse *event) { + if sysmask := uint32(mask) & e.sys.Mask; sysmask != 0 { + syse = &event{sys: unix.InotifyEvent{ + Wd: e.sys.Wd, + Mask: e.sys.Mask, + Cookie: e.sys.Cookie, + }, event: Event(sysmask), path: e.path} + } + imask := encode(mask) + switch { + case mask&Create != 0 && imask&uint32(InCreate|InMovedTo)&e.sys.Mask != 0: + e.event = Create + case mask&Remove != 0 && imask&uint32(InDelete|InDeleteSelf)&e.sys.Mask != 0: + e.event = Remove + case mask&Write != 0 && imask&uint32(InModify)&e.sys.Mask != 0: + e.event = Write + case mask&Rename != 0 && imask&uint32(InMovedFrom|InMoveSelf)&e.sys.Mask != 0: + e.event = Rename + default: + e.event = 0 + } + return +} + +// Unwatch implements notify.watcher interface. It looks for watch descriptor +// related to registered path and if found, calls inotify_rm_watch(2) function. +// This method is allowed to return EINVAL error when concurrently requested to +// delete identical path. +func (i *inotify) Unwatch(path string) (err error) { + iwd := int32(invalidDescriptor) + i.RLock() + for iwdkey, wd := range i.m { + if wd.path == path { + iwd = iwdkey + break + } + } + i.RUnlock() + if iwd == invalidDescriptor { + return errors.New("notify: path " + path + " is already watched") + } + fd := atomic.LoadInt32(&i.fd) + if err = removeInotifyWatch(fd, iwd); err != nil { + return + } + i.Lock() + delete(i.m, iwd) + i.Unlock() + return nil +} + +// Close implements notify.watcher interface. It removes all existing watch +// descriptors and wakes up producer goroutine by sending data to the write end +// of the pipe. The function waits for a signal from producer which means that +// all operations on current monitoring instance are done. +func (i *inotify) Close() (err error) { + i.Lock() + if fd := atomic.LoadInt32(&i.fd); fd == invalidDescriptor { + i.Unlock() + return nil + } + for iwd := range i.m { + if e := removeInotifyWatch(i.fd, iwd); e != nil && err == nil { + err = e + } + delete(i.m, iwd) + } + switch _, errwrite := unix.Write(i.pipefd[1], []byte{0x00}); { + case errwrite != nil && err == nil: + err = errwrite + fallthrough + case errwrite != nil: + i.Unlock() + default: + i.Unlock() + i.wg.Wait() + } + return +} + +// if path was removed, notify already removed the watch and returns EINVAL error +func removeInotifyWatch(fd int32, iwd int32) (err error) { + if _, err = unix.InotifyRmWatch(int(fd), uint32(iwd)); err != nil && err != unix.EINVAL { + return + } + return nil +} diff --git a/vendor/github.com/status-im/tcp-shaker/socket_linux.go b/vendor/github.com/status-im/tcp-shaker/socket_linux.go index f64d1c21c..c73fbe39d 100644 --- a/vendor/github.com/status-im/tcp-shaker/socket_linux.go +++ b/vendor/github.com/status-im/tcp-shaker/socket_linux.go @@ -1,4 +1,6 @@ +//go:build linux && !(android && amd64) // +build linux +// +build !android !amd64 package tcp diff --git a/vendor/github.com/status-im/tcp-shaker/socket_linux_amd.go b/vendor/github.com/status-im/tcp-shaker/socket_linux_amd.go new file mode 100644 index 000000000..b5ee218b4 --- /dev/null +++ b/vendor/github.com/status-im/tcp-shaker/socket_linux_amd.go @@ -0,0 +1,132 @@ +//go:build android && amd64 +// +build android,amd64 + +package tcp + +import ( + "fmt" + "os" + "time" + + "golang.org/x/sys/unix" +) + +const maxEpollEvents = 32 + +// createSocket creates a socket with necessary options set. +func createSocketZeroLinger(zeroLinger bool) (fd int, err error) { + // Create socket + fd, err = _createNonBlockingSocket() + if err == nil && zeroLinger { + err = _setZeroLinger(fd) + } + return +} + +// createNonBlockingSocket creates a non-blocking socket with necessary options all set. +func _createNonBlockingSocket() (int, error) { + // Create socket + fd, err := _createSocket() + if err != nil { + return 0, err + } + // Set necessary options + err = _setSockOpts(fd) + if err != nil { + unix.Close(fd) + } + return fd, err +} + +// createSocket creates a socket with CloseOnExec set +func _createSocket() (int, error) { + fd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0) + if err != nil { + return 0, os.NewSyscallError("socket", err) + } + + unix.CloseOnExec(fd) + return fd, err +} + +// setSockOpts sets SOCK_NONBLOCK and TCP_QUICKACK for given fd +func _setSockOpts(fd int) error { + err := unix.SetNonblock(fd, true) + if err != nil { + return err + } + return unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_QUICKACK, 0) +} + +var zeroLinger = unix.Linger{Onoff: 1, Linger: 0} + +// setLinger sets SO_Linger with 0 timeout to given fd +func _setZeroLinger(fd int) error { + return unix.SetsockoptLinger(fd, unix.SOL_SOCKET, unix.SO_LINGER, &zeroLinger) +} + +func createPoller() (fd int, err error) { + fd, err = unix.EpollCreate1(unix.EPOLL_CLOEXEC) + if err != nil { + err = os.NewSyscallError("epoll_create1", err) + } + return fd, err +} + +// registerEvents registers given fd with read and write events. +func registerEvents(pollerFd int, fd int) error { + var event unix.EpollEvent + event.Events = unix.EPOLLOUT | unix.EPOLLIN | unix.EPOLLET + event.Fd = int32(fd) + if err := unix.EpollCtl(pollerFd, unix.EPOLL_CTL_ADD, fd, &event); err != nil { + return os.NewSyscallError(fmt.Sprintf("epoll_ctl(%d, ADD, %d, ...)", pollerFd, fd), err) + } + return nil +} + +func pollEvents(pollerFd int, timeout time.Duration) ([]event, error) { + eventCh := make(chan event) + errorCh := make(chan error) + doneCh := make(chan bool) + + go func(fd int) { + for { + select { + case <-doneCh: + return + default: + n, _, err := unix.Recvfrom(fd, nil, unix.MSG_DONTWAIT|unix.MSG_PEEK) + if err != nil && err != unix.EAGAIN && err != unix.EWOULDBLOCK { + errorCh <- os.NewSyscallError("recvfrom", err) + return + } + if n > 0 { + eventCh <- event{Fd: fd, Err: nil} + } + time.Sleep(10 * time.Millisecond) + } + } + }(pollerFd) + + var events []event + + timer := time.NewTimer(timeout) + defer timer.Stop() + +Loop: + for { + select { + case evt := <-eventCh: + events = append(events, evt) + case err := <-errorCh: + return nil, err + case <-timer.C: + break Loop + } + } + + // Signal the goroutine to stop. + close(doneCh) + + return events, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d15db8b2e..3ffc18f52 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -871,7 +871,7 @@ github.com/remyoudompheng/bigfft # github.com/rivo/uniseg v0.2.0 ## explicit; go 1.12 github.com/rivo/uniseg -# github.com/rjeczalik/notify v0.9.3 +# github.com/rjeczalik/notify v0.9.3 => github.com/status-im/notify v1.0.2-status ## explicit; go 1.11 github.com/rjeczalik/notify # github.com/rs/cors v1.7.0 @@ -935,7 +935,7 @@ github.com/status-im/rendezvous/server # github.com/status-im/status-go/extkeys v1.1.2 ## explicit; go 1.13 github.com/status-im/status-go/extkeys -# github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501 +# github.com/status-im/tcp-shaker v1.1.1-status ## explicit; go 1.13 github.com/status-im/tcp-shaker # github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857 @@ -1396,6 +1396,7 @@ olympos.io/encoding/edn zombiezen.com/go/sqlite zombiezen.com/go/sqlite/fs zombiezen.com/go/sqlite/sqlitex +# github.com/rjeczalik/notify => github.com/status-im/notify v1.0.2-status # github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1 # github.com/nfnt/resize => github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088 # github.com/forPelevin/gomoji => github.com/status-im/gomoji v1.1.3-0.20220213022530-e5ac4a8732d4