GithubHelp home page GithubHelp logo

rjeczalik / notify Goto Github PK

View Code? Open in Web Editor NEW
884.0 33.0 123.0 749 KB

File system event notification library on steroids.

License: MIT License

Go 100.00%
golang filesystem-library notifications filesystem-events

notify's Introduction

notify's People

Contributors

adg avatar akazecik avatar calmh avatar claytono avatar codelingobot avatar cortesi avatar d6o avatar dvachaiev avatar fabiankramm avatar fjl avatar haraldnordgren avatar harshavardhana avatar imsodin avatar josharian avatar mnagel avatar nathany avatar nonsense avatar orlovevgeny avatar pblaszczyk avatar ppknap avatar rjeczalik avatar solsson avatar tie avatar toasterson avatar zillode avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

notify's Issues

Why don't you like this approach?

Please compile and run:
https://gist.github.com/ppknap/bf8d9ab54b9dc131db45

func main() {
    ch1, ch2 := make(chan<- EventInfo, 1), make(chan<- EventInfo, 1)
    Watch("p1", ch1, Create|Delete) // 1
    Watch("p1", ch2, Create) // 2
    Watch("p2", ch1, Move) // 3
    Watch("p2", ch2, Move) // 4
    Stop(ch1) // 5
}

Output:

1) Watch dir: p1, events: Create;Delete;, chan: 0xc210046000 --> Sending a new mask Create;Delete;
2) Watch dir: p1, events: Create;, chan: 0xc210046070 --> No need to update mask.
3) Watch dir: p2, events: Move;, chan: 0xc210046000 --> Sending a new mask Move;
4) Watch dir: p2, events: Move;, chan: 0xc210046070 --> No need to update mask.
5a) Stop dir: p1, chan: 0xc210046000 --> Sending a new mask Create; // we don't need to monitor Delete Event
5b) Stop dir: p2, chan: 0xc210046000 --> No need to update mask // Move is needed by chan: 0xc210046070

FSEvents: historical events strip needs more magic

--- FAIL: TestWatcherShadowedWriteCreate (1.11 seconds)
    testing_test.go:211: watcher_fsevents_test.go:100: ExpectAny received an event which does not match any of the expected ones (i=5): want one of [&notify.Call{F:"", C:(chan notify.EventInfo)(nil), P:"src/github.com/rjeczalik/fs/.fs.go.swp", NP:"", E:0x1000, NE:0x0, S:interface {}(nil)}]; got notify.Delete, 

For the following filesystem actions:

1 $ touch file
2 $ echo XD > file
3 $ echo XD > file
4 $ rm file
5 $ touch file
6 $ echo XD > file

FSEvents reports: event set (event set after strip)

  1. Create | FSEventsIsFile (Create | FSEventsIsFile)
  2. Create | Write | FSEventsInodeMetaMod | FSEventsIsFile (Write | FSEventsInodeMetaMod | FSEventsIsFile)
  3. Create | Write | FSEventsIsFile | FSEventsInodeMetaMod (Write | FSEventsIsFile | FSEventsInodeMetaMod)
  4. Write | Create | Delete | FSEventsInodeMetaMod | FSEventsIsFile (Delete | FSEventsIsFile)
  5. Create | Delete | Write | FSEventsInodeMetaMod | FSEventsIsFile (Create | Write | FSEventsInodeMetaMod | FSEventsIsFile)
  6. Create | Delete | Write | FSEventsInodeMetaMod | FSEventsIsFile (Delete | FSEventsIsFile)

Change default Watcher interface?

The common least denominator for functionality across all Watcher implementations is watching a directory, instead of watching a file and/or directory.

(Just to quickly remind: runtime uses platform-specific implementation for managing watch-points. Those implementations have different functionalities on different platforms and in order to reflect that there are couple of "extension" interfaces (traits), which are used to provide functionality like editing an existing watch-point (Rewatcher) or watching directory recursively (RecursiveWatcher). Runtime is initialised with a value of a Watcher type (base type) (NewRuntimeWatcher), and inspects whether given watcher does implement any of the traits, providing it's own implementation in case it's not.)

If it would make things simplier, I can split Watcher into DirWatcher and FileWatcher interfaces, making the former as base type and the latter as yet another trait. So we would have:

  • base type
type DirWatcher interface {
  DirWatch(path string, e Event) error
  DirUnwatch(path string) error
  Dispatch(c chan<- EventInfo, stop <-chan struct{})
}
  • traits
// If not implemented by a based type, Runtime will
// emulate it by grouping calls to DirWatch.
type FileWatcher interface {
  FileWatch(path string, e Event) error
  FileUnwatch(path string) error
}
// If not implemented by a based type, Runtime will
// emulate it by calling Unwatch followed by Watch.
type Rewatcher interface {
  Rewatch(path string, old, new Event)
}
// If not implemented by a based type, Runtime will
// emulate it by calling RecursiveUnwatch followed by RecursiveWatch.
type RecursiveRewatcher interface {
  RecursiveRewatch(oldpath, newpath string, oldevent, newevent Event)
}
// If not implemented by a based type, Runtime will
// emulate it by multiple DirWatch calls.
type RecursiveWatcher interface {
  RecursiveWatch(path string, e Event)
  RecursiveUnwatch(path string)
}

Asking, just to confirm it would simplify anything. In order to retain behaviour for existing implementations, that do not distinguish dir/file paths, it would be as simple as:

func (i *impl) DirWatch(path string, e Event) error {
  return i.watch(path, e)
}

func (i *impl) FileWatch(path string, e Event) error {
  return i.watch(path, e)
}

// ... and so on

@pblaszczyk @ppknap ?

ReadDirectoryChangesW: send on closed channel

https://ci.appveyor.com/project/rjeczalik/notify-246/build/91

=== RUN TestRecursiveTree
panic: runtime error: send on closed channel

goroutine 25 [running]:
runtime.panic(0x5ac540, 0x6db87e)
    c:/go/src/pkg/runtime/panic.c:279 +0x11f
github.com/rjeczalik/notify.(*readdcw).send(0xc0820085c0, 0xc08203a0c0, 0x1, 0x1)
    c:/projects/src/github.com/rjeczalik/notify/watcher_readdcw.go:408 +0x11b
github.com/rjeczalik/notify.(*readdcw).loopevent(0xc0820085c0, 0x22, 0xc082054600)
    c:/projects/src/github.com/rjeczalik/notify/watcher_readdcw.go:389 +0x283
github.com/rjeczalik/notify.(*readdcw).loop(0xc0820085c0)
    c:/projects/src/github.com/rjeczalik/notify/watcher_readdcw.go:336 +0x14c
created by github.com/rjeczalik/notify.(*readdcw).lazyinit
    c:/projects/src/github.com/rjeczalik/notify/watcher_readdcw.go:307 +0x15f

The dispatch channel's lifetime has not changed, yet apparently readdcw issues a dangling send. @ppknap reassign back to me if it turns out to be broken on my side.

FSEvents: sanitise paths array in gocallback

unexpected fault address 0xb01dfacedebac1e
fatal error: fault
[signal 0xb code=0x1 addr=0xb01dfacedebac1e pc=0x402857a]

goroutine 22 [running]:
runtime.throw(0x42c2ed3)
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/panic.c:520 +0x69 fp=0x43afe98 sp=0x43afe80
runtime.sigpanic()
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/os_darwin.c:457 +0x13f fp=0x43afeb0 sp=0x43afe98
runtime.findnull(0x1b00000000043c20)
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/string.goc:22 +0x1a fp=0x43afec0 sp=0x43afeb0
runtime.gostring(0x43aff18, 0x1b00000000043c20)
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/string.goc:65 +0x27 fp=0x43afef8 sp=0x43afec0
github.com/rjeczalik/notify._Cfunc_GoString(0x1b00000000043c20, 0xc20805a120, 0x1a)
        github.com/rjeczalik/notify/_test/_obj_test/_cgo_defun.c:14 +0x31 fp=0x43aff10 sp=0x43afef8
github.com/rjeczalik/notify.gocallback(0x440f790, 0xc20801a380, 0x2, 0x5000dd0, 0x43c4000, 0x43c3000)
        /Users/rjeczalik/Workspace/src/github.com/rjeczalik/notify/fsevents_cgo.go:33 +0x98 fp=0x43aff78 sp=0x43aff10
----- stack segment boundary -----
runtime.cgocallbackg1()
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/cgocall.c:275 +0xb9 fp=0x43b3f08 sp=0x43b3eb0
runtime.cgocallbackg()
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/cgocall.c:247 +0x6b fp=0x43b3f30 sp=0x43b3f08
runtime.cgocallback_gofunc(0x4004dbd, 0x4001420, 0x43b3fa8)
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/asm_amd64.s:786 +0x69 fp=0x43b3f40 sp=0x43b3f30
runtime.asmcgocall(0x4001420, 0x43b3fa8)                                                                                                                                                            [27/150]
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/asm_amd64.s:691 +0x3a fp=0x43b3f48 sp=0x43b3f40
runtime.cgocall(0x4001420, 0x43b3fa8)
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/cgocall.c:143 +0xfd fp=0x43b3f90 sp=0x43b3f48
github.com/rjeczalik/notify._Cfunc_CFRunLoopRun(0x0)
        github.com/rjeczalik/notify/_test/_obj_test/_cgo_defun.c:80 +0x31 fp=0x43b3fa8 sp=0x43b3f90
runtime.goexit()
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/proc.c:1445 fp=0x43b3fb0 sp=0x43b3fa8
created by github.com/rjeczalik/notify.(*runloop).schedule
        /Users/rjeczalik/Workspace/src/github.com/rjeczalik/notify/fsevents_cgo.go:48 +0x5e

goroutine 16 [chan receive]:
testing.RunTests(0x42190d0, 0x42c24e0, 0x19, 0x19, 0x1)
        /usr/local/gvm/gos/go1.3.3/src/pkg/testing/testing.go:505 +0x923
testing.Main(0x42190d0, 0x42c24e0, 0x19, 0x19, 0x42c5b20, 0x0, 0x0, 0x42c5b20, 0x0, 0x0)
        /usr/local/gvm/gos/go1.3.3/src/pkg/testing/testing.go:435 +0x84
main.main()
        github.com/rjeczalik/notify/_test/_testmain.go:97 +0x9c

Refactor inotify globals to be a part of struct

The issue is incomming integration tests would need to instantiate and terminate multiple watcher instances via NewWatcher. As long as inotify is implemented using single global and single syscall.InotifyInit this would break those tests.

There is no guarantee that integration tests for error handling would be capable to do full and proper cleanup of the global inotify instance, and it's obvious what happens next when a test is not idempotent to a Watcher.

Moreover running tests in parallel would not be possible without it.

collect watchpoints when underlying watch was removed outside notify

Currently it's not possible to call notify.Watch on a path that was already watched, but the watch was invalidated by delete of a file / directory, assuming the user did not clean all his watchpoints by calling notify.Stop.

Listen for Delete events for both watchpoint tree implementations and mark such watchpoint as inactive. On subsequent notify.Watch on that path reset the watch to its previous state.

FSEvents: fix TestWatcherBasic

=== RUN TestWatcherBasic
2014-12-21 14:23 notify.test[6686] (FSEvents.framework) FSEventStreamFlushSync(): failed assertion 'streamRef->appRunLoop != NULL || streamRef->event_source != NULL'

2014-12-21 14:23 notify.test[6686] (FSEvents.framework) FSEventStreamUnscheduleFromRunLoop(): failed assertion 'streamRef != NULL'

2014-12-21 14:23 notify.test[6686] (FSEvents.framework) FSEventStreamStop(): failed assertion 'streamRef != NULL'

--- FAIL: TestWatcherBasic (1.03 seconds)
    watcher.go:190: ExpectEvent test has timed out after 1s for notify.Create - github.com/rjeczalik/fs/fs_test.go (id:0)
FAIL

kqueue: race on directory write

https://travis-ci.org/rjeczalik/notify/builds/48553299

runtime.panic(0x186820, 0x20833ddd0)
    /usr/local/Cellar/go/1.3.1/libexec/src/pkg/runtime/panic.c:279 +0xf5
github.com/rjeczalik/notify.(*kqueue).monitor(0x208373110)
    /Users/travis/src/github.com/rjeczalik/notify/watcher_kqueue.go:162 +0x9a0
created by github.com/rjeczalik/notify.newWatcher
    /Users/travis/src/github.com/rjeczalik/notify/watcher_kqueue.go:27 +0x12e

@pblaszczyk Does it qualifies for a fix or should I add dispatching Error events? If the latter, the event is already in place so it's just matter of dispatching it.

Error handling

  1. Close channel and then notify.Error(ch) error:
ch := make(chan notify.EventInfo)
notify.Watch("/home/pknap/.secretporndir", ch, notify.Delete)
switch ei, ok := <-ch; {
  case ei.Event() == notify.Delete:
    fmt.Printf("Who deleted my %s file?;/\n", ei.Name())
  case !ok:
   fmt.Println("wtf: %v", notify.Error(ch))
}

[edit] The 1 option can look better:

ch := make(chan notify.EventInfo)
notify.Watch("/home/pknap/.secretporndir", ch, notify.Delete)
switch ei, ok := <-ch; ei.Event() {
  case notify.Delete:
    fmt.Printf("Who deleted my %s file?;/\n", ei.Name())
  default:
    if !ok {
       fmt.Println("wtf: %v", notify.Error(ch))
    }

}
  1. Magic unexported event info + close chan(?):
type notifyError error
func (e *notifyError) Event() Event     { ... }
func (e *notifyError) IsDir() bool       { ... }
func (e *notifyError) Name() string     { ... }
func (e *notifyError) Sys() interface{}  { ... } 
func (e *notifyError) Err() error { ... } // Extra method:>

then:

ch := make(chan notify.EventInfo)
notify.Watch("/home/pknap/rottenbodies", ch, notify.Delete)
switch ei := <-ch; ei.Event() {
  case notify.Delete:
    fmt.Printf("Who deleted my %s file?;/\n", ei.Name())
  default: // need to have all registered events in case statement or check `ei.Err() != nil` after `default`
   fmt.Println("wtf: %v", ei.Err())
}

Other ideas?

Directory tree delete and non-recursive watchers?

Removing directories recursively (either by a rm -r or os.RemoveAll) yields an event for each single item in the subtree.

  • does it work the same for recurisve and non-recursive watchers?
  • how to handle it? even if from the API viewpoint it would make sense to forward all the events to the user, from the internal implementation (Dispatch and channel tree traversing) it may be sub-optimal - instead of traversing the tree once for that just one directory and signalling deletes, we are traversing now for every single event; should it aggregate events and send one "squashed" delete for the directory which is deleted recursively?

Fsnotify behave weirdly, removing "github.com" directory from the Tree (fixture_test.go), creates the following events:

=== RUN TestIssue16
"/var/folders/gz/wkq7fzp96pl6kld9zhg4dd4r0000gn/T/notify707553153": REMOVE|WRITE
"/var/folders/gz/wkq7fzp96pl6kld9zhg4dd4r0000gn/T/notify707553153/github.com/rjeczalik/fakerpc/appveyor.yml": REMOVE
"/var/folders/gz/wkq7fzp96pl6kld9zhg4dd4r0000gn/T/notify707553153/github.com/rjeczalik/fakerpc/cli/cli.go": REMOVE
"/var/folders/gz/wkq7fzp96pl6kld9zhg4dd4r0000gn/T/notify707553153/github.com/rjeczalik/fakerpc": REMOVE|WRITE
"/var/folders/gz/wkq7fzp96pl6kld9zhg4dd4r0000gn/T/notify707553153/github.com/rjeczalik/fakerpc/cli": REMOVE|WRITE
"/var/folders/gz/wkq7fzp96pl6kld9zhg4dd4r0000gn/T/notify707553153/github.com/rjeczalik/fakerpc/cmd/fakerpc": REMOVE|WRITE
"/var/folders/gz/wkq7fzp96pl6kld9zhg4dd4r0000gn/T/notify707553153/github.com/rjeczalik/fakerpc/cmd/fakerpc/main.go": REMOVE

Note the first event is not /var/folders/gz/wkq7fzp96pl6kld9zhg4dd4r0000gn/T/notify707553153/github.com as one may expect, but actually its parent dir. Should the filter that that anomaly into account? (Actually the fsnotify.Event.Name is empty for this particular event, the path printed is just $PWD).

Open question, any ideas how Windows and Linux would handle that?

I'll revisit this after Dispatch is done.

PS You could test that by running go test -run TestIssue16 -v . (plus add prints where needed).

doc: various edge-cases worth to be mentioned

use-case: persistant watch-points

User watches a file for write events only, file gets deleted, os watcher gets removed, file eventually get created and then written, user gets the write event buts does not know it's a write on a new file.

use-case: filesystem Delete event on recursive watchpoint under Windows

If a directory being the root of recurisve watchpoint gets deleted, Windows does not create an event for this. The solution is to watch parent directory non-recursively for the delete event.

use-case: ...

...

the Watcher interface behaviour

Question @pblaszczyk @ppknap how do you see expected Watcher behaviour? TL;DR is should it be forgiving (this is mostly what stdlib does) or strict? Examples:

  • should Watcher.Watch return error when called with the same path or act as nop?
  • should Watcher.Rewatch return error when called with mismatched oldevent (e.g. a9499fe#diff-959fa682b45de8bfd370a7aef8059650R73), ignore the oldevent or act as nop?
  • etc.

This is mainly for documentation purposes, as Tree is not going to use your Watchers in that way. It may be good to define that behaviour when we would go for having every Watcher as a separate package.

Rework watcher_test to source events

When underlying implementation is able to capture more events than test expects, it fails, which is logical. Instead the test should capture all the events, and after the exec's are done it should compare the results.

--- FAIL: TestRuntimeWatcher-4 (0.01 seconds)
    watcher_test.go:68: want EventInfo{Event: notify.Create, Name: github.com/rjeczalik/fs/fs_test.go, IsDir: false}; got EventInfo{Event: notify.IN_OPEN, Name: /tmp/notify052186543, IsDir:true}
FAIL
FAIL    github.com/rjeczalik/notify 0.012s

inotify: rework to support system-dependent events

  • disable inotify behavior events (version 2?)
  • watch notify events and sys events with one watcher on >1 chans
  • inline documentation
  • README.md update
  • once again check {watcher,event}_inotify.go code (review?)
  • error handling

tree: rework merging inactive watchpoints from child to parent trees

When the same chan is used to watch both watchpoints, the child and parent one, the latter will get overwritten with inactive flag coming from the child.

Quick fix would be to drop the inactive flag for this chan, however this would require maintaining temporary unique set of user chans for each dispatch to ensure single event is not dispatched multiple times to the same channel.

The proper fix would be to rework recursiveTree to not story inactive watchpoints altogether with active ones. In order to not hack the node struct, best place would be (node).Child[""].Watch, as empty-string keys are never traversed.

HELP THE RECURSIVE EVENT DO NOT BE AN EVENT!

Currently in order to hint the notify it should watch recursively one need to append notify.Recursive event, like:

notify.Watch("/home/notify", c, notify.Create | notify.Recursive)

And yes, notify.Recursive is not an event. So if you think the idea of having it among other events is retarted, we need to pick an alternative, which is to introduce another function:

  • add notify.WatchRecursive

notify.WatchRecursive has the same signature and works the same as notify.Watch, with the only difference if path to watch is a directory, it'll be watched recursively

pros: cleaner API with not having notify.Recursive as an event
const: can't tell from notify.EventInfo if event was triggered by a recursive watch (not sure if needed)

FSEvents: test failure when NOTIFY_TMP=/tmp/notify

~ $ go test -run TestNotifyExample
PASS
ok      github.com/rjeczalik/notify     2.121s
~ $ export NOTIFY_TMP=/tmp/notify
~ $ go test -run TestNotifyExample
--- FAIL: TestNotifyExample-4 (3.04s)
        testing_test.go:605: Watch(/tmp/notify869446946/src/github.com/pblaszczyk/qttu, 0xc20805cd20, [notify.Write])=path is not being watched
        testing_test.go:211: notify_test.go:44: ExpectNotifyEvents did not receive any of the expected events [Event(notify.Write)@src/github.com/rjeczalik/fs/fs.go] after 2s (i=0)
FAIL
exit status 1
FAIL    github.com/rjeczalik/notify     3.072s

Add watcher implementation for polling

Few unknowns:

  • Should the polling interval be configurable? How? One idea is to create notify/notifyutil and hook somehow from there into notify.r runtime which would allow for controlling it
  • Should it be possible to switch implementations of watchers in run-time? E.g. as a fallback in case of errors?

tree edge-cases

  • for inotify&kqueue add Create event for every watch
  • register user c for parent recursive watchpoint as inactive
  • non-recursive watch prevents globbing for recursive one for inotify (bug)

readdcw: notify tests fail under Windows XP

Output:

F:\src\github.com\rjeczalik\notify>go test
--- FAIL: TestNotifyExample (2.58 seconds)
        testing_test.go:580: Watch(testdata\vfs.txt590602367\src\github.com\rjeczalik\fs, 0x10c96420, [notify.Write])=Niepoprawna funkcja.
        testing_test.go:580: Watch(testdata\vfs.txt590602367\src\github.com\pblaszczyk\qttu\..., 0x10c964d0, [notify.Create])=Niepoprawna funkcja.
        testing_test.go:580: Watch(testdata\vfs.txt590602367\src\github.com\pblaszczyk\qttu, 0x10c96420, [notify.Write])=Niepoprawna funkcja.
        testing_test.go:580: Watch(testdata\vfs.txt590602367\src\github.com\rjeczalik\fs\cmd\..., 0x10c96580, [notify.Delete])=Niepoprawna funkcja.
        testing_test.go:190: notify_test.go:44: ExpectNotifyEvents did not receive any of the expected events [Event(notify.Write)@src\github.com\rjeczalik\fs\fs.go] after 2s (i=0)

Rework runtime init

The idea is to have two differently initialised runtimes - one for regular package and one for tests. Obviously runtimes must be initialised exclusively. I've misunderstood how _.go works, so something else is needed.

An open problem.

inotify: support for watcher behavior masks

This functionality was disabled due to the problems with multiple chans which need different watcher behavior:

Example:

// ...
// Watch InCreate events in current working directory.
notify.Watch(".", ch1, notify.InCreate) 
// ...
// Watch InDelete events triggered **ONLY on directories** in current working folder.
notify.Watch(".", ch2, notify.InDelete, notify.InOnlydir)

kqueue: failed to read events: "interrupted system call"

Just FYI @pblaszczyk, probably it's just broken for OS X.

Steps to reproduce (sorry, no test yet).

~ $ go install github.com/rjeczalik/fs/cmd/mktree github.com/rjeczalik/fs/cmd/gotree
~ $ go install github.com/rjeczalik/notify/cmd/notify
~ $ # run in different terminal: notify $HOME/...
~ $ mkdir -p /tmp/1/2/3
~ $ touch /tmp/1/2.txt                                                                                                                            
~ $ touch /tmp/1/2/3.txt                                                                                                                          
~ $ touch /tmp/1/2/3/.txt
~ $ gotree /tmp/1 | mktree -o .

Panics with:

panic: permission denied

goroutine 20 [running]:
runtime.panic(0xe20a0, 0xd)
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/panic.c:279 +0xf5
github.com/rjeczalik/notify.func·009(0x22081e94f0, 0x20824bd60, 0x0, 0x0)
        /Users/rjeczalik/Workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go:143 +0x244
github.com/rjeczalik/notify.(*kqueue).walk(0x2081fc330, 0x208216100, 0x3a, 0x22083a0f28, 0x0, 0x0)
        /Users/rjeczalik/Workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go:299 +0x155
github.com/rjeczalik/notify.(*kqueue).monitor(0x2081fc330)
        /Users/rjeczalik/Workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go:149 +0x911
created by github.com/rjeczalik/notify.(*kqueue).Dispatch
        /Users/rjeczalik/Workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go:376 +0x41

goroutine 16 [chan receive]:
main.main()
        /Users/rjeczalik/Workspace/src/github.com/rjeczalik/notify/cmd/notify/main.go:49 +0x1b0

goroutine 19 [finalizer wait, 1 minutes]:
runtime.park(0x14ef0, 0x19b810, 0x19ab89)
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/proc.c:1369 +0x89
runtime.parkunlock(0x19b810, 0x19ab89)
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/proc.c:1385 +0x3b
runfinq()
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/mgc0.c:2644 +0xcf
runtime.goexit()
        /usr/local/gvm/gos/go1.3.3/src/pkg/runtime/proc.c:1445

goroutine 21 [select, 1 minutes]:
github.com/rjeczalik/notify.(*Tree).loopdispatch(0x2081f2280, 0x2081eb200)
        /Users/rjeczalik/Workspace/src/github.com/rjeczalik/notify/tree.go:179 +0x478
created by github.com/rjeczalik/notify.NewTree
        /Users/rjeczalik/Workspace/src/github.com/rjeczalik/notify/tree.go:217 +0x1c6

Workaround fsnotify: treat Remove | Write events as just Remove.

If a channel is registered only for Write events, the Remove events from fsnotify would get erroneously reported to the Write channel.

For OS X it looks like Remove | Write events are yielded for directory deletion and just Remove alone for a file deletion. Still it should be translated to Remove before it hits the Dispatch.

watcher: support recursive -> non-recursive and vice versa rewatching

Rewatch and RecursiveRewatch should support two more transitions.

Currently passing equal paths and eventsets to both is a nop. Instead it should change the watch state to non-recursive for the former and to recursive for the latter.

// modifies watch under "path" to be non-recursive
watcher.Rewatch("path", Create, Create)

// modifies watch under "path" to be recursive
watcher.RecursiveRewatch("path", "path", Create, Create)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.