PageRenderTime 126ms CodeModel.GetById 32ms RepoModel.GetById 5ms app.codeStats 0ms

/vendor/github.com/fsnotify/fsnotify/kqueue.go

https://gitlab.com/github-cloud-corporation/mayday
Go | 503 lines | 356 code | 72 blank | 75 comment | 107 complexity | 6875881eebe8ae731e52e2d3020e24de MD5 | raw file
  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 freebsd openbsd netbsd dragonfly darwin
  5. package fsnotify
  6. import (
  7. "errors"
  8. "fmt"
  9. "io/ioutil"
  10. "os"
  11. "path/filepath"
  12. "sync"
  13. "time"
  14. "golang.org/x/sys/unix"
  15. )
  16. // Watcher watches a set of files, delivering events to a channel.
  17. type Watcher struct {
  18. Events chan Event
  19. Errors chan error
  20. done chan bool // Channel for sending a "quit message" to the reader goroutine
  21. kq int // File descriptor (as returned by the kqueue() syscall).
  22. mu sync.Mutex // Protects access to watcher data
  23. watches map[string]int // Map of watched file descriptors (key: path).
  24. externalWatches map[string]bool // Map of watches added by user of the library.
  25. dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue.
  26. paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events.
  27. fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events).
  28. isClosed bool // Set to true when Close() is first called
  29. }
  30. type pathInfo struct {
  31. name string
  32. isDir bool
  33. }
  34. // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
  35. func NewWatcher() (*Watcher, error) {
  36. kq, err := kqueue()
  37. if err != nil {
  38. return nil, err
  39. }
  40. w := &Watcher{
  41. kq: kq,
  42. watches: make(map[string]int),
  43. dirFlags: make(map[string]uint32),
  44. paths: make(map[int]pathInfo),
  45. fileExists: make(map[string]bool),
  46. externalWatches: make(map[string]bool),
  47. Events: make(chan Event),
  48. Errors: make(chan error),
  49. done: make(chan bool),
  50. }
  51. go w.readEvents()
  52. return w, nil
  53. }
  54. // Close removes all watches and closes the events channel.
  55. func (w *Watcher) Close() error {
  56. w.mu.Lock()
  57. if w.isClosed {
  58. w.mu.Unlock()
  59. return nil
  60. }
  61. w.isClosed = true
  62. w.mu.Unlock()
  63. // copy paths to remove while locked
  64. w.mu.Lock()
  65. var pathsToRemove = make([]string, 0, len(w.watches))
  66. for name := range w.watches {
  67. pathsToRemove = append(pathsToRemove, name)
  68. }
  69. w.mu.Unlock()
  70. // unlock before calling Remove, which also locks
  71. var err error
  72. for _, name := range pathsToRemove {
  73. if e := w.Remove(name); e != nil && err == nil {
  74. err = e
  75. }
  76. }
  77. // Send "quit" message to the reader goroutine:
  78. w.done <- true
  79. return nil
  80. }
  81. // Add starts watching the named file or directory (non-recursively).
  82. func (w *Watcher) Add(name string) error {
  83. w.mu.Lock()
  84. w.externalWatches[name] = true
  85. w.mu.Unlock()
  86. _, err := w.addWatch(name, noteAllEvents)
  87. return err
  88. }
  89. // Remove stops watching the the named file or directory (non-recursively).
  90. func (w *Watcher) Remove(name string) error {
  91. name = filepath.Clean(name)
  92. w.mu.Lock()
  93. watchfd, ok := w.watches[name]
  94. w.mu.Unlock()
  95. if !ok {
  96. return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
  97. }
  98. const registerRemove = unix.EV_DELETE
  99. if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil {
  100. return err
  101. }
  102. unix.Close(watchfd)
  103. w.mu.Lock()
  104. isDir := w.paths[watchfd].isDir
  105. delete(w.watches, name)
  106. delete(w.paths, watchfd)
  107. delete(w.dirFlags, name)
  108. w.mu.Unlock()
  109. // Find all watched paths that are in this directory that are not external.
  110. if isDir {
  111. var pathsToRemove []string
  112. w.mu.Lock()
  113. for _, path := range w.paths {
  114. wdir, _ := filepath.Split(path.name)
  115. if filepath.Clean(wdir) == name {
  116. if !w.externalWatches[path.name] {
  117. pathsToRemove = append(pathsToRemove, path.name)
  118. }
  119. }
  120. }
  121. w.mu.Unlock()
  122. for _, name := range pathsToRemove {
  123. // Since these are internal, not much sense in propagating error
  124. // to the user, as that will just confuse them with an error about
  125. // a path they did not explicitly watch themselves.
  126. w.Remove(name)
  127. }
  128. }
  129. return nil
  130. }
  131. // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
  132. const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
  133. // keventWaitTime to block on each read from kevent
  134. var keventWaitTime = durationToTimespec(100 * time.Millisecond)
  135. // addWatch adds name to the watched file set.
  136. // The flags are interpreted as described in kevent(2).
  137. // Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
  138. func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
  139. var isDir bool
  140. // Make ./name and name equivalent
  141. name = filepath.Clean(name)
  142. w.mu.Lock()
  143. if w.isClosed {
  144. w.mu.Unlock()
  145. return "", errors.New("kevent instance already closed")
  146. }
  147. watchfd, alreadyWatching := w.watches[name]
  148. // We already have a watch, but we can still override flags.
  149. if alreadyWatching {
  150. isDir = w.paths[watchfd].isDir
  151. }
  152. w.mu.Unlock()
  153. if !alreadyWatching {
  154. fi, err := os.Lstat(name)
  155. if err != nil {
  156. return "", err
  157. }
  158. // Don't watch sockets.
  159. if fi.Mode()&os.ModeSocket == os.ModeSocket {
  160. return "", nil
  161. }
  162. // Don't watch named pipes.
  163. if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
  164. return "", nil
  165. }
  166. // Follow Symlinks
  167. // Unfortunately, Linux can add bogus symlinks to watch list without
  168. // issue, and Windows can't do symlinks period (AFAIK). To maintain
  169. // consistency, we will act like everything is fine. There will simply
  170. // be no file events for broken symlinks.
  171. // Hence the returns of nil on errors.
  172. if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
  173. name, err = filepath.EvalSymlinks(name)
  174. if err != nil {
  175. return "", nil
  176. }
  177. w.mu.Lock()
  178. _, alreadyWatching = w.watches[name]
  179. w.mu.Unlock()
  180. if alreadyWatching {
  181. return name, nil
  182. }
  183. fi, err = os.Lstat(name)
  184. if err != nil {
  185. return "", nil
  186. }
  187. }
  188. watchfd, err = unix.Open(name, openMode, 0700)
  189. if watchfd == -1 {
  190. return "", err
  191. }
  192. isDir = fi.IsDir()
  193. }
  194. const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE
  195. if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil {
  196. unix.Close(watchfd)
  197. return "", err
  198. }
  199. if !alreadyWatching {
  200. w.mu.Lock()
  201. w.watches[name] = watchfd
  202. w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
  203. w.mu.Unlock()
  204. }
  205. if isDir {
  206. // Watch the directory if it has not been watched before,
  207. // or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
  208. w.mu.Lock()
  209. watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
  210. (!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
  211. // Store flags so this watch can be updated later
  212. w.dirFlags[name] = flags
  213. w.mu.Unlock()
  214. if watchDir {
  215. if err := w.watchDirectoryFiles(name); err != nil {
  216. return "", err
  217. }
  218. }
  219. }
  220. return name, nil
  221. }
  222. // readEvents reads from kqueue and converts the received kevents into
  223. // Event values that it sends down the Events channel.
  224. func (w *Watcher) readEvents() {
  225. eventBuffer := make([]unix.Kevent_t, 10)
  226. for {
  227. // See if there is a message on the "done" channel
  228. select {
  229. case <-w.done:
  230. err := unix.Close(w.kq)
  231. if err != nil {
  232. w.Errors <- err
  233. }
  234. close(w.Events)
  235. close(w.Errors)
  236. return
  237. default:
  238. }
  239. // Get new events
  240. kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
  241. // EINTR is okay, the syscall was interrupted before timeout expired.
  242. if err != nil && err != unix.EINTR {
  243. w.Errors <- err
  244. continue
  245. }
  246. // Flush the events we received to the Events channel
  247. for len(kevents) > 0 {
  248. kevent := &kevents[0]
  249. watchfd := int(kevent.Ident)
  250. mask := uint32(kevent.Fflags)
  251. w.mu.Lock()
  252. path := w.paths[watchfd]
  253. w.mu.Unlock()
  254. event := newEvent(path.name, mask)
  255. if path.isDir && !(event.Op&Remove == Remove) {
  256. // Double check to make sure the directory exists. This can happen when
  257. // we do a rm -fr on a recursively watched folders and we receive a
  258. // modification event first but the folder has been deleted and later
  259. // receive the delete event
  260. if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
  261. // mark is as delete event
  262. event.Op |= Remove
  263. }
  264. }
  265. if event.Op&Rename == Rename || event.Op&Remove == Remove {
  266. w.Remove(event.Name)
  267. w.mu.Lock()
  268. delete(w.fileExists, event.Name)
  269. w.mu.Unlock()
  270. }
  271. if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) {
  272. w.sendDirectoryChangeEvents(event.Name)
  273. } else {
  274. // Send the event on the Events channel
  275. w.Events <- event
  276. }
  277. if event.Op&Remove == Remove {
  278. // Look for a file that may have overwritten this.
  279. // For example, mv f1 f2 will delete f2, then create f2.
  280. if path.isDir {
  281. fileDir := filepath.Clean(event.Name)
  282. w.mu.Lock()
  283. _, found := w.watches[fileDir]
  284. w.mu.Unlock()
  285. if found {
  286. // make sure the directory exists before we watch for changes. When we
  287. // do a recursive watch and perform rm -fr, the parent directory might
  288. // have gone missing, ignore the missing directory and let the
  289. // upcoming delete event remove the watch from the parent directory.
  290. if _, err := os.Lstat(fileDir); err == nil {
  291. w.sendDirectoryChangeEvents(fileDir)
  292. }
  293. }
  294. } else {
  295. filePath := filepath.Clean(event.Name)
  296. if fileInfo, err := os.Lstat(filePath); err == nil {
  297. w.sendFileCreatedEventIfNew(filePath, fileInfo)
  298. }
  299. }
  300. }
  301. // Move to next event
  302. kevents = kevents[1:]
  303. }
  304. }
  305. }
  306. // newEvent returns an platform-independent Event based on kqueue Fflags.
  307. func newEvent(name string, mask uint32) Event {
  308. e := Event{Name: name}
  309. if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
  310. e.Op |= Remove
  311. }
  312. if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
  313. e.Op |= Write
  314. }
  315. if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
  316. e.Op |= Rename
  317. }
  318. if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
  319. e.Op |= Chmod
  320. }
  321. return e
  322. }
  323. func newCreateEvent(name string) Event {
  324. return Event{Name: name, Op: Create}
  325. }
  326. // watchDirectoryFiles to mimic inotify when adding a watch on a directory
  327. func (w *Watcher) watchDirectoryFiles(dirPath string) error {
  328. // Get all files
  329. files, err := ioutil.ReadDir(dirPath)
  330. if err != nil {
  331. return err
  332. }
  333. for _, fileInfo := range files {
  334. filePath := filepath.Join(dirPath, fileInfo.Name())
  335. filePath, err = w.internalWatch(filePath, fileInfo)
  336. if err != nil {
  337. return err
  338. }
  339. w.mu.Lock()
  340. w.fileExists[filePath] = true
  341. w.mu.Unlock()
  342. }
  343. return nil
  344. }
  345. // sendDirectoryEvents searches the directory for newly created files
  346. // and sends them over the event channel. This functionality is to have
  347. // the BSD version of fsnotify match Linux inotify which provides a
  348. // create event for files created in a watched directory.
  349. func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
  350. // Get all files
  351. files, err := ioutil.ReadDir(dirPath)
  352. if err != nil {
  353. w.Errors <- err
  354. }
  355. // Search for new files
  356. for _, fileInfo := range files {
  357. filePath := filepath.Join(dirPath, fileInfo.Name())
  358. err := w.sendFileCreatedEventIfNew(filePath, fileInfo)
  359. if err != nil {
  360. return
  361. }
  362. }
  363. }
  364. // sendFileCreatedEvent sends a create event if the file isn't already being tracked.
  365. func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
  366. w.mu.Lock()
  367. _, doesExist := w.fileExists[filePath]
  368. w.mu.Unlock()
  369. if !doesExist {
  370. // Send create event
  371. w.Events <- newCreateEvent(filePath)
  372. }
  373. // like watchDirectoryFiles (but without doing another ReadDir)
  374. filePath, err = w.internalWatch(filePath, fileInfo)
  375. if err != nil {
  376. return err
  377. }
  378. w.mu.Lock()
  379. w.fileExists[filePath] = true
  380. w.mu.Unlock()
  381. return nil
  382. }
  383. func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
  384. if fileInfo.IsDir() {
  385. // mimic Linux providing delete events for subdirectories
  386. // but preserve the flags used if currently watching subdirectory
  387. w.mu.Lock()
  388. flags := w.dirFlags[name]
  389. w.mu.Unlock()
  390. flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
  391. return w.addWatch(name, flags)
  392. }
  393. // watch file to mimic Linux inotify
  394. return w.addWatch(name, noteAllEvents)
  395. }
  396. // kqueue creates a new kernel event queue and returns a descriptor.
  397. func kqueue() (kq int, err error) {
  398. kq, err = unix.Kqueue()
  399. if kq == -1 {
  400. return kq, err
  401. }
  402. return kq, nil
  403. }
  404. // register events with the queue
  405. func register(kq int, fds []int, flags int, fflags uint32) error {
  406. changes := make([]unix.Kevent_t, len(fds))
  407. for i, fd := range fds {
  408. // SetKevent converts int to the platform-specific types:
  409. unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
  410. changes[i].Fflags = fflags
  411. }
  412. // register the events
  413. success, err := unix.Kevent(kq, changes, nil, nil)
  414. if success == -1 {
  415. return err
  416. }
  417. return nil
  418. }
  419. // read retrieves pending events, or waits until an event occurs.
  420. // A timeout of nil blocks indefinitely, while 0 polls the queue.
  421. func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) {
  422. n, err := unix.Kevent(kq, nil, events, timeout)
  423. if err != nil {
  424. return nil, err
  425. }
  426. return events[0:n], nil
  427. }
  428. // durationToTimespec prepares a timeout value
  429. func durationToTimespec(d time.Duration) unix.Timespec {
  430. return unix.NsecToTimespec(d.Nanoseconds())
  431. }