inotify.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build linux
  5. package fsnotify
  6. import (
  7. "errors"
  8. "fmt"
  9. "io"
  10. "os"
  11. "path/filepath"
  12. "strings"
  13. "sync"
  14. "syscall"
  15. "unsafe"
  16. )
  17. // Watcher watches a set of files, delivering events to a channel.
  18. type Watcher struct {
  19. Events chan Event
  20. Errors chan error
  21. mu sync.Mutex // Map access
  22. fd int
  23. poller *fdPoller
  24. watches map[string]*watch // Map of inotify watches (key: path)
  25. paths map[int]string // Map of watched paths (key: watch descriptor)
  26. done chan struct{} // Channel for sending a "quit message" to the reader goroutine
  27. doneResp chan struct{} // Channel to respond to Close
  28. }
  29. // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
  30. func NewWatcher() (*Watcher, error) {
  31. // Create inotify fd
  32. fd, errno := syscall.InotifyInit()
  33. if fd == -1 {
  34. return nil, errno
  35. }
  36. // Create epoll
  37. poller, err := newFdPoller(fd)
  38. if err != nil {
  39. syscall.Close(fd)
  40. return nil, err
  41. }
  42. w := &Watcher{
  43. fd: fd,
  44. poller: poller,
  45. watches: make(map[string]*watch),
  46. paths: make(map[int]string),
  47. Events: make(chan Event),
  48. Errors: make(chan error),
  49. done: make(chan struct{}),
  50. doneResp: make(chan struct{}),
  51. }
  52. go w.readEvents()
  53. return w, nil
  54. }
  55. func (w *Watcher) isClosed() bool {
  56. select {
  57. case <-w.done:
  58. return true
  59. default:
  60. return false
  61. }
  62. }
  63. // Close removes all watches and closes the events channel.
  64. func (w *Watcher) Close() error {
  65. if w.isClosed() {
  66. return nil
  67. }
  68. // Send 'close' signal to goroutine, and set the Watcher to closed.
  69. close(w.done)
  70. // Wake up goroutine
  71. w.poller.wake()
  72. // Wait for goroutine to close
  73. <-w.doneResp
  74. return nil
  75. }
  76. // Add starts watching the named file or directory (non-recursively).
  77. func (w *Watcher) Add(name string) error {
  78. name = filepath.Clean(name)
  79. if w.isClosed() {
  80. return errors.New("inotify instance already closed")
  81. }
  82. const agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM |
  83. syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY |
  84. syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF
  85. var flags uint32 = agnosticEvents
  86. w.mu.Lock()
  87. watchEntry, found := w.watches[name]
  88. w.mu.Unlock()
  89. if found {
  90. watchEntry.flags |= flags
  91. flags |= syscall.IN_MASK_ADD
  92. }
  93. wd, errno := syscall.InotifyAddWatch(w.fd, name, flags)
  94. if wd == -1 {
  95. return errno
  96. }
  97. w.mu.Lock()
  98. w.watches[name] = &watch{wd: uint32(wd), flags: flags}
  99. w.paths[wd] = name
  100. w.mu.Unlock()
  101. return nil
  102. }
  103. // Remove stops watching the named file or directory (non-recursively).
  104. func (w *Watcher) Remove(name string) error {
  105. name = filepath.Clean(name)
  106. // Fetch the watch.
  107. w.mu.Lock()
  108. defer w.mu.Unlock()
  109. watch, ok := w.watches[name]
  110. // Remove it from inotify.
  111. if !ok {
  112. return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
  113. }
  114. // inotify_rm_watch will return EINVAL if the file has been deleted;
  115. // the inotify will already have been removed.
  116. // That means we can safely delete it from our watches, whatever inotify_rm_watch does.
  117. delete(w.watches, name)
  118. success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
  119. if success == -1 {
  120. // TODO: Perhaps it's not helpful to return an error here in every case.
  121. // the only two possible errors are:
  122. // EBADF, which happens when w.fd is not a valid file descriptor of any kind.
  123. // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
  124. // Watch descriptors are invalidated when they are removed explicitly or implicitly;
  125. // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
  126. return errno
  127. }
  128. return nil
  129. }
  130. type watch struct {
  131. wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
  132. flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
  133. }
  134. // readEvents reads from the inotify file descriptor, converts the
  135. // received events into Event objects and sends them via the Events channel
  136. func (w *Watcher) readEvents() {
  137. var (
  138. buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
  139. n int // Number of bytes read with read()
  140. errno error // Syscall errno
  141. ok bool // For poller.wait
  142. )
  143. defer close(w.doneResp)
  144. defer close(w.Errors)
  145. defer close(w.Events)
  146. defer syscall.Close(w.fd)
  147. defer w.poller.close()
  148. for {
  149. // See if we have been closed.
  150. if w.isClosed() {
  151. return
  152. }
  153. ok, errno = w.poller.wait()
  154. if errno != nil {
  155. select {
  156. case w.Errors <- errno:
  157. case <-w.done:
  158. return
  159. }
  160. continue
  161. }
  162. if !ok {
  163. continue
  164. }
  165. n, errno = syscall.Read(w.fd, buf[:])
  166. // If a signal interrupted execution, see if we've been asked to close, and try again.
  167. // http://man7.org/linux/man-pages/man7/signal.7.html :
  168. // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
  169. if errno == syscall.EINTR {
  170. continue
  171. }
  172. // syscall.Read might have been woken up by Close. If so, we're done.
  173. if w.isClosed() {
  174. return
  175. }
  176. if n < syscall.SizeofInotifyEvent {
  177. var err error
  178. if n == 0 {
  179. // If EOF is received. This should really never happen.
  180. err = io.EOF
  181. } else if n < 0 {
  182. // If an error occured while reading.
  183. err = errno
  184. } else {
  185. // Read was too short.
  186. err = errors.New("notify: short read in readEvents()")
  187. }
  188. select {
  189. case w.Errors <- err:
  190. case <-w.done:
  191. return
  192. }
  193. continue
  194. }
  195. var offset uint32
  196. // We don't know how many events we just read into the buffer
  197. // While the offset points to at least one whole event...
  198. for offset <= uint32(n-syscall.SizeofInotifyEvent) {
  199. // Point "raw" to the event in the buffer
  200. raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
  201. mask := uint32(raw.Mask)
  202. nameLen := uint32(raw.Len)
  203. // If the event happened to the watched directory or the watched file, the kernel
  204. // doesn't append the filename to the event, but we would like to always fill the
  205. // the "Name" field with a valid filename. We retrieve the path of the watch from
  206. // the "paths" map.
  207. w.mu.Lock()
  208. name := w.paths[int(raw.Wd)]
  209. w.mu.Unlock()
  210. if nameLen > 0 {
  211. // Point "bytes" at the first byte of the filename
  212. bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
  213. // The filename is padded with NULL bytes. TrimRight() gets rid of those.
  214. name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
  215. }
  216. event := newEvent(name, mask)
  217. // Send the events that are not ignored on the events channel
  218. if !event.ignoreLinux(mask) {
  219. select {
  220. case w.Events <- event:
  221. case <-w.done:
  222. return
  223. }
  224. }
  225. // Move to the next event in the buffer
  226. offset += syscall.SizeofInotifyEvent + nameLen
  227. }
  228. }
  229. }
  230. // Certain types of events can be "ignored" and not sent over the Events
  231. // channel. Such as events marked ignore by the kernel, or MODIFY events
  232. // against files that do not exist.
  233. func (e *Event) ignoreLinux(mask uint32) bool {
  234. // Ignore anything the inotify API says to ignore
  235. if mask&syscall.IN_IGNORED == syscall.IN_IGNORED {
  236. return true
  237. }
  238. // If the event is not a DELETE or RENAME, the file must exist.
  239. // Otherwise the event is ignored.
  240. // *Note*: this was put in place because it was seen that a MODIFY
  241. // event was sent after the DELETE. This ignores that MODIFY and
  242. // assumes a DELETE will come or has come if the file doesn't exist.
  243. if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
  244. _, statErr := os.Lstat(e.Name)
  245. return os.IsNotExist(statErr)
  246. }
  247. return false
  248. }
  249. // newEvent returns an platform-independent Event based on an inotify mask.
  250. func newEvent(name string, mask uint32) Event {
  251. e := Event{Name: name}
  252. if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO {
  253. e.Op |= Create
  254. }
  255. if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE {
  256. e.Op |= Remove
  257. }
  258. if mask&syscall.IN_MODIFY == syscall.IN_MODIFY {
  259. e.Op |= Write
  260. }
  261. if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM {
  262. e.Op |= Rename
  263. }
  264. if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB {
  265. e.Op |= Chmod
  266. }
  267. return e
  268. }