inotify_linux.go 8.4 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. /*
  5. Package inotify implements a wrapper for the Linux inotify system.
  6. Example:
  7. watcher, err := inotify.NewWatcher()
  8. if err != nil {
  9. log.Fatal(err)
  10. }
  11. err = watcher.Watch("/tmp")
  12. if err != nil {
  13. log.Fatal(err)
  14. }
  15. for {
  16. select {
  17. case ev := <-watcher.Event:
  18. log.Println("event:", ev)
  19. case err := <-watcher.Error:
  20. log.Println("error:", err)
  21. }
  22. }
  23. */
  24. package inotify // import "github.com/sigma/go-inotify"
  25. import (
  26. "errors"
  27. "fmt"
  28. "os"
  29. "strings"
  30. "sync"
  31. "syscall"
  32. "unsafe"
  33. )
  34. type Event struct {
  35. Mask uint32 // Mask of events
  36. Cookie uint32 // Unique cookie associating related events (for rename(2))
  37. Name string // File name (optional)
  38. }
  39. type watch struct {
  40. wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
  41. flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
  42. }
  43. type Watcher struct {
  44. mu sync.Mutex
  45. fd int // File descriptor (as returned by the inotify_init() syscall)
  46. watches map[string]*watch // Map of inotify watches (key: path)
  47. paths map[int]string // Map of watched paths (key: watch descriptor)
  48. Error chan error // Errors are sent on this channel
  49. Event chan *Event // Events are returned on this channel
  50. done chan bool // Channel for sending a "quit message" to the reader goroutine
  51. isClosed bool // Set to true when Close() is first called
  52. }
  53. // NewWatcher creates and returns a new inotify instance using inotify_init(2)
  54. func NewWatcher() (*Watcher, error) {
  55. fd, errno := syscall.InotifyInit()
  56. if fd == -1 {
  57. return nil, os.NewSyscallError("inotify_init", errno)
  58. }
  59. w := &Watcher{
  60. fd: fd,
  61. watches: make(map[string]*watch),
  62. paths: make(map[int]string),
  63. Event: make(chan *Event),
  64. Error: make(chan error),
  65. done: make(chan bool, 1),
  66. }
  67. go w.readEvents()
  68. return w, nil
  69. }
  70. // Close closes an inotify watcher instance
  71. // It sends a message to the reader goroutine to quit and removes all watches
  72. // associated with the inotify instance
  73. func (w *Watcher) Close() error {
  74. if w.isClosed {
  75. return nil
  76. }
  77. w.isClosed = true
  78. // Send "quit" message to the reader goroutine
  79. w.done <- true
  80. for path := range w.watches {
  81. w.RemoveWatch(path)
  82. }
  83. return nil
  84. }
  85. // AddWatch adds path to the watched file set.
  86. // The flags are interpreted as described in inotify_add_watch(2).
  87. func (w *Watcher) AddWatch(path string, flags uint32) error {
  88. if w.isClosed {
  89. return errors.New("inotify instance already closed")
  90. }
  91. watchEntry, found := w.watches[path]
  92. if found {
  93. watchEntry.flags |= flags
  94. flags |= syscall.IN_MASK_ADD
  95. }
  96. w.mu.Lock() // synchronize with readEvents goroutine
  97. wd, err := syscall.InotifyAddWatch(w.fd, path, flags)
  98. if err != nil {
  99. w.mu.Unlock()
  100. return &os.PathError{
  101. Op: "inotify_add_watch",
  102. Path: path,
  103. Err: err,
  104. }
  105. }
  106. if !found {
  107. w.watches[path] = &watch{wd: uint32(wd), flags: flags}
  108. w.paths[wd] = path
  109. }
  110. w.mu.Unlock()
  111. return nil
  112. }
  113. // Watch adds path to the watched file set, watching all events.
  114. func (w *Watcher) Watch(path string) error {
  115. return w.AddWatch(path, IN_ALL_EVENTS)
  116. }
  117. // RemoveWatch removes path from the watched file set.
  118. func (w *Watcher) RemoveWatch(path string) error {
  119. watch, ok := w.watches[path]
  120. if !ok {
  121. return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path))
  122. }
  123. success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
  124. if success == -1 {
  125. return os.NewSyscallError("inotify_rm_watch", errno)
  126. }
  127. delete(w.watches, path)
  128. // Locking here to protect the read from paths in readEvents.
  129. w.mu.Lock()
  130. delete(w.paths, int(watch.wd))
  131. w.mu.Unlock()
  132. return nil
  133. }
  134. // readEvents reads from the inotify file descriptor, converts the
  135. // received events into Event objects and sends them via the Event channel
  136. func (w *Watcher) readEvents() {
  137. var buf [syscall.SizeofInotifyEvent * 4096]byte
  138. for {
  139. n, err := syscall.Read(w.fd, buf[:])
  140. // See if there is a message on the "done" channel
  141. var done bool
  142. select {
  143. case done = <-w.done:
  144. default:
  145. }
  146. // If EOF or a "done" message is received
  147. if n == 0 || done {
  148. // The syscall.Close can be slow. Close
  149. // w.Event first.
  150. close(w.Event)
  151. err := syscall.Close(w.fd)
  152. if err != nil {
  153. w.Error <- os.NewSyscallError("close", err)
  154. }
  155. close(w.Error)
  156. return
  157. }
  158. if n < 0 {
  159. w.Error <- os.NewSyscallError("read", err)
  160. continue
  161. }
  162. if n < syscall.SizeofInotifyEvent {
  163. w.Error <- errors.New("inotify: short read in readEvents()")
  164. continue
  165. }
  166. var offset uint32 = 0
  167. // We don't know how many events we just read into the buffer
  168. // While the offset points to at least one whole event...
  169. for offset <= uint32(n-syscall.SizeofInotifyEvent) {
  170. // Point "raw" to the event in the buffer
  171. raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
  172. event := new(Event)
  173. event.Mask = uint32(raw.Mask)
  174. event.Cookie = uint32(raw.Cookie)
  175. nameLen := uint32(raw.Len)
  176. // If the event happened to the watched directory or the watched file, the kernel
  177. // doesn't append the filename to the event, but we would like to always fill the
  178. // the "Name" field with a valid filename. We retrieve the path of the watch from
  179. // the "paths" map.
  180. w.mu.Lock()
  181. name, ok := w.paths[int(raw.Wd)]
  182. w.mu.Unlock()
  183. if ok {
  184. event.Name = name
  185. if nameLen > 0 {
  186. // Point "bytes" at the first byte of the filename
  187. bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
  188. // The filename is padded with NUL bytes. TrimRight() gets rid of those.
  189. event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
  190. }
  191. // Send the event on the events channel
  192. w.Event <- event
  193. }
  194. // Move to the next event in the buffer
  195. offset += syscall.SizeofInotifyEvent + nameLen
  196. }
  197. }
  198. }
  199. // String formats the event e in the form
  200. // "filename: 0xEventMask = IN_ACCESS|IN_ATTRIB_|..."
  201. func (e *Event) String() string {
  202. var events string = ""
  203. m := e.Mask
  204. for _, b := range eventBits {
  205. if m&b.Value == b.Value {
  206. m &^= b.Value
  207. events += "|" + b.Name
  208. }
  209. }
  210. if m != 0 {
  211. events += fmt.Sprintf("|%#x", m)
  212. }
  213. if len(events) > 0 {
  214. events = " == " + events[1:]
  215. }
  216. return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events)
  217. }
  218. const (
  219. // Options for inotify_init() are not exported
  220. // IN_CLOEXEC uint32 = syscall.IN_CLOEXEC
  221. // IN_NONBLOCK uint32 = syscall.IN_NONBLOCK
  222. // Options for AddWatch
  223. IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
  224. IN_ONESHOT uint32 = syscall.IN_ONESHOT
  225. IN_ONLYDIR uint32 = syscall.IN_ONLYDIR
  226. // The "IN_MASK_ADD" option is not exported, as AddWatch
  227. // adds it automatically, if there is already a watch for the given path
  228. // IN_MASK_ADD uint32 = syscall.IN_MASK_ADD
  229. // Events
  230. IN_ACCESS uint32 = syscall.IN_ACCESS
  231. IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS
  232. IN_ATTRIB uint32 = syscall.IN_ATTRIB
  233. IN_CLOSE uint32 = syscall.IN_CLOSE
  234. IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
  235. IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE
  236. IN_CREATE uint32 = syscall.IN_CREATE
  237. IN_DELETE uint32 = syscall.IN_DELETE
  238. IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF
  239. IN_MODIFY uint32 = syscall.IN_MODIFY
  240. IN_MOVE uint32 = syscall.IN_MOVE
  241. IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM
  242. IN_MOVED_TO uint32 = syscall.IN_MOVED_TO
  243. IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF
  244. IN_OPEN uint32 = syscall.IN_OPEN
  245. // Special events
  246. IN_ISDIR uint32 = syscall.IN_ISDIR
  247. IN_IGNORED uint32 = syscall.IN_IGNORED
  248. IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
  249. IN_UNMOUNT uint32 = syscall.IN_UNMOUNT
  250. )
  251. var eventBits = []struct {
  252. Value uint32
  253. Name string
  254. }{
  255. {IN_ACCESS, "IN_ACCESS"},
  256. {IN_ATTRIB, "IN_ATTRIB"},
  257. {IN_CLOSE, "IN_CLOSE"},
  258. {IN_CLOSE_NOWRITE, "IN_CLOSE_NOWRITE"},
  259. {IN_CLOSE_WRITE, "IN_CLOSE_WRITE"},
  260. {IN_CREATE, "IN_CREATE"},
  261. {IN_DELETE, "IN_DELETE"},
  262. {IN_DELETE_SELF, "IN_DELETE_SELF"},
  263. {IN_MODIFY, "IN_MODIFY"},
  264. {IN_MOVE, "IN_MOVE"},
  265. {IN_MOVED_FROM, "IN_MOVED_FROM"},
  266. {IN_MOVED_TO, "IN_MOVED_TO"},
  267. {IN_MOVE_SELF, "IN_MOVE_SELF"},
  268. {IN_OPEN, "IN_OPEN"},
  269. {IN_ISDIR, "IN_ISDIR"},
  270. {IN_IGNORED, "IN_IGNORED"},
  271. {IN_Q_OVERFLOW, "IN_Q_OVERFLOW"},
  272. {IN_UNMOUNT, "IN_UNMOUNT"},
  273. }