Update dependencies

This commit is contained in:
bluepython508
2025-04-09 01:00:12 +01:00
parent f0641ffd6e
commit 5a9cfc022c
882 changed files with 68930 additions and 24201 deletions

View File

@@ -1,67 +0,0 @@
## Gonotify
Simple Golang inotify wrapper.
[![GoDoc](https://godoc.org/github.com/illarion/gonotify/v2?status.svg)](https://godoc.org/github.com/illarion/gonotify/v2)
### Provides following primitives:
* Low level
* `Inotify` - wrapper around [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html)
* `InotifyEvent` - generated file/folder event. Contains `Name` (full path), watch descriptior and `Mask` that describes the event.
* Higher level
* `FileWatcher` - higher level utility, helps to watch the list of files for changes, creation or removal
* `DirWatcher` - higher level utility, recursively watches given root folder for added, removed or changed files.
* `FileEvent` - embeds `InotifyEvent` and keeps additional field `Eof` to notify user that there will be no more events.
Use `FileWatcher` and `DirWatcher` as an example and build your own utility classes.
### Usage
```go
package main
import (
"fmt"
"github.com/illarion/gonotify/v2"
"time"
"context"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
watcher, err := gonotify.NewDirWatcher(ctx, gonotify.IN_CREATE|gonotify.IN_CLOSE, "/tmp")
if err != nil {
panic(err)
}
for {
select {
case event := <-watcher.C:
fmt.Printf("Event: %s\n", event)
if event.Mask&gonotify.IN_CREATE != 0 {
fmt.Printf("File created: %s\n", event.Name)
}
if event.Mask&gonotify.IN_CLOSE != 0 {
fmt.Printf("File closed: %s\n", event.Name)
}
case <-time.After(5 * time.Second):
fmt.Println("Timeout")
cancel()
return
}
}
}
```
## License
MIT. See LICENSE file for more details.

View File

@@ -1,290 +0,0 @@
//go:build linux
// +build linux
package gonotify
import (
"context"
"errors"
"fmt"
"path/filepath"
"strings"
"syscall"
"time"
"unsafe"
)
// max number of events to read at once
const maxEvents = 1024
var TimeoutError = errors.New("Inotify timeout")
type getWatchRequest struct {
pathName string
result chan uint32
}
type getPathRequest struct {
wd uint32
result chan string
}
type addWatchRequest struct {
pathName string
wd uint32
}
// Inotify is the low level wrapper around inotify_init(), inotify_add_watch() and inotify_rm_watch()
type Inotify struct {
// ctx is the context of inotify instance
ctx context.Context
// fd is the file descriptor of inotify instance
fd int
// getWatchByPathIn is the channel for getting watch descriptor by path
getWatchByPathIn chan getWatchRequest
// getPathByWatchIn is the channel for getting path by watch descriptor
getPathByWatchIn chan getPathRequest
// addWatchIn is the channel for adding watch
addWatchIn chan addWatchRequest
// rmByWdIn is the channel for removing watch by watch descriptor
rmByWdIn chan uint32
// rmByPathIn is the channel for removing watch by path
rmByPathIn chan string
}
// NewInotify creates new inotify instance
func NewInotify(ctx context.Context) (*Inotify, error) {
fd, err := syscall.InotifyInit1(syscall.IN_CLOEXEC | syscall.IN_NONBLOCK)
if err != nil {
return nil, err
}
inotify := &Inotify{
ctx: ctx,
fd: fd,
getPathByWatchIn: make(chan getPathRequest),
getWatchByPathIn: make(chan getWatchRequest),
addWatchIn: make(chan addWatchRequest),
rmByWdIn: make(chan uint32),
rmByPathIn: make(chan string),
}
go func() {
watches := make(map[string]uint32)
paths := make(map[uint32]string)
for {
select {
case <-ctx.Done():
for _, w := range watches {
_, err := syscall.InotifyRmWatch(fd, w)
if err != nil {
continue
}
}
syscall.Close(fd)
return
case req := <-inotify.addWatchIn:
watches[req.pathName] = req.wd
paths[req.wd] = req.pathName
case req := <-inotify.getWatchByPathIn:
wd, ok := watches[req.pathName]
if !ok {
close(req.result)
}
req.result <- wd
close(req.result)
case req := <-inotify.getPathByWatchIn:
pathName, ok := paths[req.wd]
if !ok {
close(req.result)
}
req.result <- pathName
close(req.result)
case wd := <-inotify.rmByWdIn:
pathName, ok := paths[wd]
if !ok {
continue
}
delete(watches, pathName)
delete(paths, wd)
case pathName := <-inotify.rmByPathIn:
wd, ok := watches[pathName]
if !ok {
continue
}
delete(watches, pathName)
delete(paths, wd)
}
}
}()
return inotify, nil
}
// AddWatch adds given path to list of watched files / folders
func (i *Inotify) AddWatch(pathName string, mask uint32) error {
w, err := syscall.InotifyAddWatch(i.fd, pathName, mask)
if err != nil {
return err
}
select {
case <-i.ctx.Done():
return i.ctx.Err()
case i.addWatchIn <- addWatchRequest{
pathName: pathName,
wd: uint32(w)}:
return nil
}
}
// RmWd removes watch by watch descriptor
func (i *Inotify) RmWd(wd uint32) error {
select {
case <-i.ctx.Done():
return i.ctx.Err()
case i.rmByWdIn <- wd:
return nil
}
}
// RmWatch removes watch by pathName
func (i *Inotify) RmWatch(pathName string) error {
select {
case <-i.ctx.Done():
return i.ctx.Err()
case i.rmByPathIn <- pathName:
return nil
}
}
// Read reads portion of InotifyEvents and may fail with an error. If no events are available, it will
// wait forever, until context is cancelled.
func (i *Inotify) Read() ([]InotifyEvent, error) {
for {
evts, err := i.ReadDeadline(time.Now().Add(time.Millisecond * 200))
if err != nil {
if err == TimeoutError {
continue
}
return evts, err
}
if len(evts) > 0 {
return evts, nil
}
}
}
// ReadDeadline waits for InotifyEvents until deadline is reached, or context is cancelled. If
// deadline is reached, TimeoutError is returned.
func (i *Inotify) ReadDeadline(deadline time.Time) ([]InotifyEvent, error) {
events := make([]InotifyEvent, 0, maxEvents)
buf := make([]byte, maxEvents*(syscall.SizeofInotifyEvent+syscall.NAME_MAX+1))
var n int
var err error
fdset := &syscall.FdSet{}
//main:
for {
if i.ctx.Err() != nil {
return events, i.ctx.Err()
}
now := time.Now()
if now.After(deadline) {
return events, TimeoutError
}
diff := deadline.Sub(now)
timeout := syscall.NsecToTimeval(diff.Nanoseconds())
fdset.Bits[0] = 1 << uint(i.fd)
_, err = syscall.Select(i.fd+1, fdset, nil, nil, &timeout)
if err != nil {
if err == syscall.EINTR {
continue
}
return events, err
}
if fdset.Bits[0]&(1<<uint(i.fd)) == 0 {
continue // No data to read, continue waiting
}
n, err = syscall.Read(i.fd, buf)
if err != nil {
if err == syscall.EAGAIN {
continue
}
return events, err
}
if n > 0 {
break
}
}
if n < syscall.SizeofInotifyEvent {
return events, fmt.Errorf("short inotify read, expected at least one SizeofInotifyEvent %d, got %d", syscall.SizeofInotifyEvent, n)
}
offset := 0
for offset+syscall.SizeofInotifyEvent <= n {
event := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
var name string
{
nameStart := offset + syscall.SizeofInotifyEvent
nameEnd := offset + syscall.SizeofInotifyEvent + int(event.Len)
if nameEnd > n {
return events, fmt.Errorf("corrupted inotify event length %d", event.Len)
}
name = strings.TrimRight(string(buf[nameStart:nameEnd]), "\x00")
offset = nameEnd
}
req := getPathRequest{
wd: uint32(event.Wd),
result: make(chan string),
}
select {
case <-i.ctx.Done():
return events, i.ctx.Err()
case i.getPathByWatchIn <- req:
select {
case <-i.ctx.Done():
return events, i.ctx.Err()
case watchName := <-req.result:
name = filepath.Join(watchName, name)
}
}
events = append(events, InotifyEvent{
Wd: uint32(event.Wd),
Name: name,
Mask: event.Mask,
Cookie: event.Cookie,
})
}
return events, nil
}

71
vendor/github.com/illarion/gonotify/v3/README.md generated vendored Normal file
View File

@@ -0,0 +1,71 @@
## Gonotify
Simple Golang inotify wrapper.
[![GoDoc](https://godoc.org/github.com/illarion/gonotify/v3?status.svg)](https://godoc.org/github.com/illarion/gonotify/v3)
### Provides following primitives:
* Low level
* `Inotify` - wrapper around [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html)
* `InotifyEvent` - generated file/folder event. Contains `Name` (full path), `Wd` - watch descriptor and `Mask` that describes the event.
* Higher level
* `FileWatcher` - higher level utility, helps to watch the list of files for changes, creation or removal
* `DirWatcher` - higher level utility, recursively watches given root folder for added, removed or changed files.
* `FileEvent` - embeds `InotifyEvent` and keeps additional field `Eof` to notify user that there will be no more events.
Use `FileWatcher` and `DirWatcher` as an example and build your own utility classes.
### Usage
```go
package main
import (
"fmt"
"github.com/illarion/gonotify/v3"
"time"
"context"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
watcher, err := gonotify.NewDirWatcher(ctx, gonotify.IN_CREATE|gonotify.IN_CLOSE, "/tmp")
if err != nil {
panic(err)
}
main:
for {
select {
case event := <-watcher.C:
fmt.Printf("Event: %s\n", event)
if event.Is(gonotify.IN_CREATE) {
fmt.Printf("File created: %s\n", event.Name)
}
if event.IsAny(gonotify.IN_CLOSE, gonotify.IN_CLOSE_WRITE) {
fmt.Printf("File closed: %s\n", event.Name)
}
case <-time.After(5 * time.Second):
fmt.Println("Good bye!")
cancel()
break main
}
}
// Wait for watcher to finish all internal goroutines
<-watcher.Done()
fmt.Println("Watcher is done")
}
```
## License
MIT. See [LICENSE](LICENSE) file for more details.

View File

@@ -4,27 +4,27 @@ import (
"context"
"os"
"path/filepath"
"sync"
)
// DirWatcher recursively watches the given root folder, waiting for file events.
// Events can be masked by providing fileMask. DirWatcher does not generate events for
// folders or subfolders.
type DirWatcher struct {
C chan FileEvent
C chan FileEvent
done chan struct{}
}
// NewDirWatcher creates DirWatcher recursively waiting for events in the given root folder and
// emitting FileEvents in channel C, that correspond to fileMask. Folder events are ignored (having IN_ISDIR set to 1)
func NewDirWatcher(ctx context.Context, fileMask uint32, root string) (*DirWatcher, error) {
dw := &DirWatcher{
C: make(chan FileEvent),
C: make(chan FileEvent),
done: make(chan struct{}),
}
ctx, cancel := context.WithCancel(ctx)
i, err := NewInotify(ctx)
if err != nil {
cancel()
return nil, err
}
@@ -48,30 +48,54 @@ func NewDirWatcher(ctx context.Context, fileMask uint32, root string) (*DirWatch
return nil
}
return i.AddWatch(path, IN_ALL_EVENTS)
_, err = i.AddWatch(path, IN_ALL_EVENTS)
return err
})
if err != nil {
cancel()
return nil, err
}
events := make(chan FileEvent)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
for _, event := range queue {
events <- event
select {
case <-ctx.Done():
close(events)
return
case events <- event:
}
}
queue = nil
for {
select {
case <-ctx.Done():
close(events)
return
default:
}
raw, err := i.Read()
if err != nil {
close(events)
return
}
select {
case <-ctx.Done():
close(events)
return
default:
}
for _, event := range raw {
// Skip ignored events queued from removed watchers
@@ -92,11 +116,15 @@ func NewDirWatcher(ctx context.Context, fileMask uint32, root string) (*DirWatch
if !f.IsDir() {
// fake event, but there can be duplicates of this event provided by real watcher
events <- FileEvent{
select {
case <-ctx.Done():
return nil
case events <- FileEvent{
InotifyEvent: InotifyEvent{
Name: path,
Mask: IN_CREATE,
},
}: //noop
}
}
@@ -120,24 +148,44 @@ func NewDirWatcher(ctx context.Context, fileMask uint32, root string) (*DirWatch
continue
}
events <- FileEvent{
select {
case <-ctx.Done():
return
case events <- FileEvent{
InotifyEvent: event,
}: //noop
}
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
defer close(dw.C)
for {
select {
case <-ctx.Done():
return
// drain events
for {
select {
case _, ok := <-events:
if !ok {
return
}
default:
return
}
}
case event, ok := <-events:
if !ok {
dw.C <- FileEvent{
select {
case <-ctx.Done():
case dw.C <- FileEvent{
Eof: true,
}:
}
cancel()
return
}
@@ -146,11 +194,25 @@ func NewDirWatcher(ctx context.Context, fileMask uint32, root string) (*DirWatch
continue
}
dw.C <- event
select {
case dw.C <- event:
case <-ctx.Done():
return
}
}
}
}()
return dw, nil
go func() {
wg.Wait()
<-i.Done()
close(dw.done)
}()
return dw, nil
}
// Done returns a channel that is closed when DirWatcher is done
func (dw *DirWatcher) Done() <-chan struct{} {
return dw.done
}

View File

@@ -81,7 +81,7 @@ func InMaskToString(in_mask uint32) string {
// InotifyEvent is the go representation of inotify_event found in sys/inotify.h
type InotifyEvent struct {
// Watch descriptor
Wd uint32
Wd int
// File or directory name
Name string
// Contains bits that describe the event that occurred

View File

@@ -3,35 +3,34 @@ package gonotify
import (
"context"
"path/filepath"
"sync"
)
// FileWatcher waits for events generated by filesystem for a specific list of file paths, including
// IN_CREATE for not yet existing files and IN_DELETE for removed.
type FileWatcher struct {
C chan FileEvent
C chan FileEvent
done chan struct{}
}
// NewFileWatcher creates FileWatcher with provided inotify mask and list of files to wait events for.
func NewFileWatcher(ctx context.Context, mask uint32, files ...string) (*FileWatcher, error) {
f := &FileWatcher{
C: make(chan FileEvent),
C: make(chan FileEvent),
done: make(chan struct{}),
}
ctx, cancel := context.WithCancel(ctx)
inotify, err := NewInotify(ctx)
if err != nil {
cancel()
return nil, err
}
expectedPaths := make(map[string]bool)
for _, file := range files {
err := inotify.AddWatch(filepath.Dir(file), mask)
_, err := inotify.AddWatch(filepath.Dir(file), mask)
if err != nil {
cancel()
return nil, err
}
expectedPaths[file] = true
@@ -39,11 +38,20 @@ func NewFileWatcher(ctx context.Context, mask uint32, files ...string) (*FileWat
events := make(chan FileEvent)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer cancel()
defer wg.Done()
for {
raw, err := inotify.Read()
select {
case <-ctx.Done():
close(events)
return
default:
}
raw, err := inotify.Read()
if err != nil {
close(events)
return
@@ -61,8 +69,10 @@ func NewFileWatcher(ctx context.Context, mask uint32, files ...string) (*FileWat
}
}()
wg.Add(1)
go func() {
defer cancel()
defer wg.Done()
defer close(f.C)
for {
select {
case <-ctx.Done():
@@ -70,8 +80,9 @@ func NewFileWatcher(ctx context.Context, mask uint32, files ...string) (*FileWat
case event, ok := <-events:
if !ok {
f.C <- FileEvent{
Eof: true,
select {
case <-ctx.Done():
case f.C <- FileEvent{Eof: true}:
}
return
}
@@ -80,10 +91,25 @@ func NewFileWatcher(ctx context.Context, mask uint32, files ...string) (*FileWat
continue
}
f.C <- event
select {
case <-ctx.Done():
return
case f.C <- event:
}
}
}
}()
go func() {
<-inotify.Done()
wg.Wait()
close(f.done)
}()
return f, nil
}
// Done returns a channel that is closed when the FileWatcher is done.
func (f *FileWatcher) Done() <-chan struct{} {
return f.done
}

527
vendor/github.com/illarion/gonotify/v3/inotify.go generated vendored Normal file
View File

@@ -0,0 +1,527 @@
//go:build linux
// +build linux
package gonotify
import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"sync"
"syscall"
"time"
"unsafe"
"github.com/illarion/gonotify/v3/syscallf"
)
const (
// maxEvents is the maximum number of events to read in one syscall
maxEvents = 1024
)
type addWatchResult struct {
wd int
err error
}
type addWatchRequest struct {
pathName string
mask uint32
result chan addWatchResult
}
type rmWdRequest struct {
wd int
ignored bool // if true, the watch was removed automatically
result chan error
}
type rmPathRequest struct {
pathName string
result chan error
}
type eventItem struct {
InotifyEvent
err error
}
// Inotify is the low level wrapper around inotify_init(), inotify_add_watch() and inotify_rm_watch()
type Inotify struct {
ctx context.Context
done chan struct{}
addWatchIn chan addWatchRequest
rmByWdIn chan rmWdRequest
rmByPathIn chan rmPathRequest
eventsOut chan eventItem
readMutex sync.Mutex
}
// NewInotify creates new inotify instance
func NewInotify(ctx context.Context) (*Inotify, error) {
fd, err := syscall.InotifyInit1(syscall.IN_CLOEXEC | syscall.IN_NONBLOCK)
if err != nil {
return nil, err
}
file := os.NewFile(uintptr(fd), "inotify")
inotify := &Inotify{
ctx: ctx,
done: make(chan struct{}),
addWatchIn: make(chan addWatchRequest),
rmByWdIn: make(chan rmWdRequest),
rmByPathIn: make(chan rmPathRequest),
eventsOut: make(chan eventItem, maxEvents),
}
type getPathRequest struct {
wd int
result chan string
}
getPathIn := make(chan getPathRequest)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
//defer cancel()
<-ctx.Done()
//file.Close()
wg.Done()
}()
wg.Add(1)
// read events goroutine. Only this goroutine can read or close the inotify file descriptor
go func() {
defer wg.Done()
defer close(inotify.eventsOut)
// reusable buffers for reading inotify events. Make sure they're not
// leaked into other goroutines, as they're not thread safe
buf := make([]byte, maxEvents*(syscall.SizeofInotifyEvent+syscall.NAME_MAX+1))
for {
select {
case <-ctx.Done():
return
default:
}
var n int
for {
select {
case <-ctx.Done():
return
default:
}
n, err = file.Read(buf)
if err != nil {
// if we got an error, we should return
select {
case inotify.eventsOut <- eventItem{
InotifyEvent: InotifyEvent{},
err: err,
}:
default:
}
return
}
if n > 0 {
break
}
}
if n < syscall.SizeofInotifyEvent {
select {
case <-ctx.Done():
return
default:
continue
}
}
offset := 0
for offset+syscall.SizeofInotifyEvent <= n {
event := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
var name string
{
nameStart := offset + syscall.SizeofInotifyEvent
nameEnd := offset + syscall.SizeofInotifyEvent + int(event.Len)
if nameEnd > n {
continue
}
name = strings.TrimRight(string(buf[nameStart:nameEnd]), "\x00")
offset = nameEnd
}
req := getPathRequest{wd: int(event.Wd), result: make(chan string)}
var watchName string
select {
case <-ctx.Done():
return
case getPathIn <- req:
select {
case <-ctx.Done():
return
case watchName = <-req.result:
}
}
if watchName == "" {
continue
}
name = filepath.Join(watchName, name)
inotifyEvent := InotifyEvent{
Wd: int(event.Wd),
Name: name,
Mask: event.Mask,
Cookie: event.Cookie,
}
// watch was removed explicitly or automatically
if inotifyEvent.Is(IN_IGNORED) {
// remove watch
result := make(chan error)
select {
case <-ctx.Done():
return
case inotify.rmByWdIn <- rmWdRequest{
wd: int(event.Wd),
ignored: true,
result: result,
}:
case <-time.After(1 * time.Second):
}
select {
case <-ctx.Done():
return
case err := <-result:
if err != nil {
// TODO log error
}
}
continue
}
select {
case <-ctx.Done():
return
case inotify.eventsOut <- eventItem{
InotifyEvent: inotifyEvent,
err: nil,
}:
}
}
}
}()
wg.Add(1)
// main goroutine (handle channels)
go func() {
//defer cancel()
defer wg.Done()
watches := make(map[string]int)
paths := make(map[int]string)
for {
select {
case <-ctx.Done():
// Handle pending requests
draining := true
for draining {
select {
case req := <-inotify.addWatchIn:
// Send error to addWatch requests
select {
case req.result <- addWatchResult{
wd: 0,
err: errors.New("Inotify instance closed"),
}:
default:
}
case <-inotify.rmByWdIn:
case <-inotify.addWatchIn:
case <-inotify.rmByPathIn:
case <-getPathIn:
default:
draining = false
}
}
for _, w := range watches {
_, err := syscallf.InotifyRmWatch(fd, w)
if err != nil {
continue
}
}
return
case req := <-inotify.addWatchIn:
wd, err := syscall.InotifyAddWatch(fd, req.pathName, req.mask)
if err == nil {
watches[req.pathName] = wd
paths[wd] = req.pathName
}
select {
case req.result <- addWatchResult{wd: wd, err: err}:
case <-ctx.Done():
}
case req := <-inotify.rmByWdIn:
pathName, ok := paths[req.wd]
if !ok {
continue
}
if !req.ignored {
_, err = syscallf.InotifyRmWatch(fd, req.wd)
}
delete(watches, pathName)
delete(paths, req.wd)
select {
case req.result <- err:
case <-ctx.Done():
}
case req := <-inotify.rmByPathIn:
wd, ok := watches[req.pathName]
if !ok {
continue
}
_, err := syscallf.InotifyRmWatch(fd, wd)
delete(watches, req.pathName)
delete(paths, wd)
select {
case req.result <- err:
case <-ctx.Done():
}
case req := <-getPathIn:
wd := paths[req.wd]
select {
case req.result <- wd:
case <-ctx.Done():
}
}
}
}()
go func() {
//defer cancel()
wg.Wait()
close(inotify.done)
}()
return inotify, nil
}
// Done returns a channel that is closed when Inotify is done
func (i *Inotify) Done() <-chan struct{} {
return i.done
}
// AddWatch adds given path to list of watched files / folders
func (i *Inotify) AddWatch(pathName string, mask uint32) (int, error) {
req := addWatchRequest{
pathName: pathName,
mask: mask,
result: make(chan addWatchResult),
}
select {
case <-i.ctx.Done():
return 0, i.ctx.Err()
case i.addWatchIn <- req:
select {
case <-i.ctx.Done():
return 0, i.ctx.Err()
case result := <-req.result:
return result.wd, result.err
}
}
}
// RmWd removes watch by watch descriptor
func (i *Inotify) RmWd(wd int) error {
req := rmWdRequest{
wd: wd,
ignored: false,
result: make(chan error),
}
select {
case <-i.ctx.Done():
return i.ctx.Err()
case i.rmByWdIn <- req:
}
select {
case <-i.ctx.Done():
return i.ctx.Err()
case err := <-req.result:
return err
}
}
// RmWatch removes watch by pathName
func (i *Inotify) RmWatch(pathName string) error {
req := rmPathRequest{
pathName: pathName,
result: make(chan error),
}
select {
case <-i.ctx.Done():
return i.ctx.Err()
case i.rmByPathIn <- req:
}
select {
case <-i.ctx.Done():
return i.ctx.Err()
case err := <-req.result:
return err
}
}
// Read reads portion of InotifyEvents and may fail with an error. If no events are available, it will
// wait forever, until context is cancelled.
func (i *Inotify) Read() ([]InotifyEvent, error) {
i.readMutex.Lock()
defer i.readMutex.Unlock()
events := make([]InotifyEvent, 0, maxEvents)
select {
case <-i.ctx.Done():
return events, i.ctx.Err()
case <-i.Done():
return events, errors.New("inotify closed")
case evt, ok := <-i.eventsOut:
if !ok {
return events, errors.New("inotify closed")
}
if evt.err != nil {
return events, evt.err
}
if evt.InotifyEvent.Wd != 0 {
// append first event
events = append(events, evt.InotifyEvent)
}
if len(events) >= maxEvents {
return events, nil
}
// read all available events
read:
for {
select {
case <-i.ctx.Done():
return events, i.ctx.Err()
case <-i.Done():
return events, errors.New("inotify closed")
case evt, ok := <-i.eventsOut:
if !ok {
return events, errors.New("inotify closed")
}
if evt.err != nil {
return events, evt.err
}
if evt.InotifyEvent.Wd != 0 {
// append event
events = append(events, evt.InotifyEvent)
}
if len(events) >= maxEvents {
return events, nil
}
default:
break read
}
}
}
return events, nil
}
// ReadDeadline waits for InotifyEvents until deadline is reached, or context is cancelled. If
// deadline is reached, it will return all events read until that point.
func (i *Inotify) ReadDeadline(deadline time.Time) ([]InotifyEvent, error) {
i.readMutex.Lock()
defer i.readMutex.Unlock()
events := make([]InotifyEvent, 0, maxEvents)
for {
select {
case <-i.ctx.Done():
return events, i.ctx.Err()
case <-i.Done():
return events, errors.New("Inotify closed")
case <-time.After(time.Until(deadline)):
return events, nil
case evt, ok := <-i.eventsOut:
if !ok {
return events, errors.New("Inotify closed")
}
if evt.err != nil {
return events, evt.err
}
events = append(events, evt.InotifyEvent)
if len(events) >= maxEvents {
return events, nil
}
}
}
}

View File

@@ -0,0 +1,17 @@
//go:build linux
package syscallf
import "syscall"
func InotifyRmWatch(fd int, watchdesc int) (int, error) {
var success int
var err error
r0, _, e1 := syscall.RawSyscall(syscall.SYS_INOTIFY_RM_WATCH, uintptr(fd), uintptr(watchdesc), 0)
success = int(r0)
if e1 != 0 {
err = e1
}
return success, err
}