1 // Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
18 "golang.org/x/sys/unix"
21 // eventBufferSize defines the size of the buffer given to read(2) function. One
22 // should not depend on this value, since it was arbitrary chosen and may be
23 // changed in the future.
24 const eventBufferSize = 64 * (unix.SizeofInotifyEvent + unix.PathMax + 1)
26 // consumersCount defines the number of consumers in producer-consumer based
27 // implementation. Each consumer is run in a separate goroutine and has read
28 // access to watched files map.
29 const consumersCount = 2
31 const invalidDescriptor = -1
33 // watched is a pair of file path and inotify mask used as a value in
40 // inotify implements Watcher interface.
42 sync.RWMutex // protects inotify.m map
43 m map[int32]*watched // watch descriptor to watched object
44 fd int32 // inotify file descriptor
45 pipefd []int // pipe's read and write descriptors
46 epfd int // epoll descriptor
47 epes []unix.EpollEvent // epoll events
48 buffer [eventBufferSize]byte // inotify event buffer
49 wg sync.WaitGroup // wait group used to close main loop
50 c chan<- EventInfo // event dispatcher channel
53 // NewWatcher creates new non-recursive inotify backed by inotify.
54 func newWatcher(c chan<- EventInfo) watcher {
56 m: make(map[int32]*watched),
57 fd: invalidDescriptor,
58 pipefd: []int{invalidDescriptor, invalidDescriptor},
59 epfd: invalidDescriptor,
60 epes: make([]unix.EpollEvent, 0),
63 runtime.SetFinalizer(i, func(i *inotify) {
65 if i.fd != invalidDescriptor {
72 // Watch implements notify.watcher interface.
73 func (i *inotify) Watch(path string, e Event) error {
74 return i.watch(path, e)
77 // Rewatch implements notify.watcher interface.
78 func (i *inotify) Rewatch(path string, _, newevent Event) error {
79 return i.watch(path, newevent)
82 // watch adds a new watcher to the set of watched objects or modifies the existing
83 // one. If called for the first time, this function initializes inotify filesystem
84 // monitor and starts producer-consumers goroutines.
85 func (i *inotify) watch(path string, e Event) (err error) {
86 if e&^(All|Event(unix.IN_ALL_EVENTS)) != 0 {
87 return errors.New("notify: unknown event")
89 if err = i.lazyinit(); err != nil {
92 iwd, err := unix.InotifyAddWatch(int(i.fd), path, encode(e))
101 if i.m[int32(iwd)] == nil {
102 i.m[int32(iwd)] = &watched{path: path, mask: uint32(e)}
113 // lazyinit sets up all required file descriptors and starts 1+consumersCount
114 // goroutines. The producer goroutine blocks until file-system notifications
115 // occur. Then, all events are read from system buffer and sent to consumer
116 // goroutines which construct valid notify events. This method uses
117 // Double-Checked Locking optimization.
118 func (i *inotify) lazyinit() error {
119 if atomic.LoadInt32(&i.fd) == invalidDescriptor {
122 if atomic.LoadInt32(&i.fd) == invalidDescriptor {
123 fd, err := unix.InotifyInit1(unix.IN_CLOEXEC)
128 if err = i.epollinit(); err != nil {
129 _, _ = i.epollclose(), unix.Close(int(fd)) // Ignore errors.
130 i.fd = invalidDescriptor
133 esch := make(chan []*event)
135 i.wg.Add(consumersCount)
136 for n := 0; n < consumersCount; n++ {
144 // epollinit opens an epoll file descriptor and creates a pipe which will be
145 // used to wake up the epoll_wait(2) function. Then, file descriptor associated
146 // with inotify event queue and the read end of the pipe are added to epoll set.
147 // Note that `fd` member must be set before this function is called.
148 func (i *inotify) epollinit() (err error) {
149 if i.epfd, err = unix.EpollCreate1(0); err != nil {
152 if err = unix.Pipe(i.pipefd); err != nil {
155 i.epes = []unix.EpollEvent{
156 {Events: unix.EPOLLIN, Fd: i.fd},
157 {Events: unix.EPOLLIN, Fd: int32(i.pipefd[0])},
159 if err = unix.EpollCtl(i.epfd, unix.EPOLL_CTL_ADD, int(i.fd), &i.epes[0]); err != nil {
162 return unix.EpollCtl(i.epfd, unix.EPOLL_CTL_ADD, i.pipefd[0], &i.epes[1])
165 // epollclose closes the file descriptor created by the call to epoll_create(2)
166 // and two file descriptors opened by pipe(2) function.
167 func (i *inotify) epollclose() (err error) {
168 if i.epfd != invalidDescriptor {
169 if err = unix.Close(i.epfd); err == nil {
170 i.epfd = invalidDescriptor
173 for n, fd := range i.pipefd {
174 if fd != invalidDescriptor {
175 switch e := unix.Close(fd); {
176 case e != nil && err == nil:
179 i.pipefd[n] = invalidDescriptor
186 // loop blocks until either inotify or pipe file descriptor is ready for I/O.
187 // All read operations triggered by filesystem notifications are forwarded to
188 // one of the event's consumers. If pipe fd became ready, loop function closes
189 // all file descriptors opened by lazyinit method and returns afterwards.
190 func (i *inotify) loop(esch chan<- []*event) {
191 epes := make([]unix.EpollEvent, 1)
192 fd := atomic.LoadInt32(&i.fd)
194 switch _, err := unix.EpollWait(i.epfd, epes, -1); err {
200 case int32(i.pipefd[0]):
203 if err = unix.Close(int(fd)); err != nil && err != unix.EINTR {
204 panic("notify: close(2) error " + err.Error())
206 atomic.StoreInt32(&i.fd, invalidDescriptor)
207 if err = i.epollclose(); err != nil && err != unix.EINTR {
208 panic("notify: epollclose error " + err.Error())
215 default: // We should never reach this line.
216 panic("notify: epoll_wait(2) error " + err.Error())
221 // read reads events from an inotify file descriptor. It does not handle errors
222 // returned from read(2) function since they are not critical to watcher logic.
223 func (i *inotify) read() (es []*event) {
224 n, err := unix.Read(int(i.fd), i.buffer[:])
225 if err != nil || n < unix.SizeofInotifyEvent {
228 var sys *unix.InotifyEvent
229 nmin := n - unix.SizeofInotifyEvent
230 for pos, path := 0, ""; pos <= nmin; {
231 sys = (*unix.InotifyEvent)(unsafe.Pointer(&i.buffer[pos]))
232 pos += unix.SizeofInotifyEvent
233 if path = ""; sys.Len > 0 {
234 endpos := pos + int(sys.Len)
235 path = string(bytes.TrimRight(i.buffer[pos:endpos], "\x00"))
238 es = append(es, &event{
239 sys: unix.InotifyEvent{
250 // send is a consumer function which sends events to event dispatcher channel.
251 // It is run in a separate goroutine in order to not block loop method when
252 // possibly expensive write operations are performed on inotify map.
253 func (i *inotify) send(esch <-chan []*event) {
254 for es := range esch {
255 for _, e := range i.transform(es) {
264 // transform prepares events read from inotify file descriptor for sending to
265 // user. It removes invalid events and these which are no longer present in
266 // inotify map. This method may also split one raw event into two different ones
267 // when system-dependent result is required.
268 func (i *inotify) transform(es []*event) []*event {
271 for idx, e := range es {
272 if e.sys.Mask&(unix.IN_IGNORED|unix.IN_Q_OVERFLOW) != 0 {
276 wd, ok := i.m[e.sys.Wd]
277 if !ok || e.sys.Mask&encode(Event(wd.mask)) == 0 {
284 e.path = filepath.Join(wd.path, e.path)
286 multi = append(multi, decode(Event(wd.mask), e))
292 es = append(es, multi...)
296 // encode converts notify system-independent events to valid inotify mask
297 // which can be passed to inotify_add_watch(2) function.
298 func encode(e Event) uint32 {
300 e = (e ^ Create) | InCreate | InMovedTo
303 e = (e ^ Remove) | InDelete | InDeleteSelf
306 e = (e ^ Write) | InModify
309 e = (e ^ Rename) | InMovedFrom | InMoveSelf
314 // decode uses internally stored mask to distinguish whether system-independent
315 // or system-dependent event is requested. The first one is created by modifying
316 // `e` argument. decode method sets e.event value to 0 when an event should be
317 // skipped. System-dependent event is set as the function's return value which
318 // can be nil when the event should not be passed on.
319 func decode(mask Event, e *event) (syse *event) {
320 if sysmask := uint32(mask) & e.sys.Mask; sysmask != 0 {
321 syse = &event{sys: unix.InotifyEvent{
324 Cookie: e.sys.Cookie,
325 }, event: Event(sysmask), path: e.path}
327 imask := encode(mask)
329 case mask&Create != 0 && imask&uint32(InCreate|InMovedTo)&e.sys.Mask != 0:
331 case mask&Remove != 0 && imask&uint32(InDelete|InDeleteSelf)&e.sys.Mask != 0:
333 case mask&Write != 0 && imask&uint32(InModify)&e.sys.Mask != 0:
335 case mask&Rename != 0 && imask&uint32(InMovedFrom|InMoveSelf)&e.sys.Mask != 0:
343 // Unwatch implements notify.watcher interface. It looks for watch descriptor
344 // related to registered path and if found, calls inotify_rm_watch(2) function.
345 // This method is allowed to return EINVAL error when concurrently requested to
346 // delete identical path.
347 func (i *inotify) Unwatch(path string) (err error) {
348 iwd := int32(invalidDescriptor)
350 for iwdkey, wd := range i.m {
357 if iwd == invalidDescriptor {
358 return errors.New("notify: path " + path + " is already watched")
360 fd := atomic.LoadInt32(&i.fd)
361 if _, err = unix.InotifyRmWatch(int(fd), uint32(iwd)); err != nil {
370 // Close implements notify.watcher interface. It removes all existing watch
371 // descriptors and wakes up producer goroutine by sending data to the write end
372 // of the pipe. The function waits for a signal from producer which means that
373 // all operations on current monitoring instance are done.
374 func (i *inotify) Close() (err error) {
376 if fd := atomic.LoadInt32(&i.fd); fd == invalidDescriptor {
380 for iwd := range i.m {
381 if _, e := unix.InotifyRmWatch(int(i.fd), uint32(iwd)); e != nil && err == nil {
386 switch _, errwrite := unix.Write(i.pipefd[1], []byte{0x00}); {
387 case errwrite != nil && err == nil:
390 case errwrite != nil: