watcher_kqueue.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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.
  4. // +build darwin,kqueue dragonfly freebsd netbsd openbsd
  5. package notify
  6. import (
  7. "fmt"
  8. "os"
  9. "syscall"
  10. )
  11. // newTrigger returns implementation of trigger.
  12. func newTrigger(pthLkp map[string]*watched) trigger {
  13. return &kq{
  14. pthLkp: pthLkp,
  15. idLkp: make(map[int]*watched),
  16. }
  17. }
  18. // kq is a structure implementing trigger for kqueue.
  19. type kq struct {
  20. // fd is a kqueue file descriptor
  21. fd int
  22. // pipefds are file descriptors used to stop `Kevent` call.
  23. pipefds [2]int
  24. // idLkp is a data structure mapping file descriptors with data about watching
  25. // represented by them files/directories.
  26. idLkp map[int]*watched
  27. // pthLkp is a structure mapping monitored files/dir with data about them,
  28. // shared with parent trg structure
  29. pthLkp map[string]*watched
  30. }
  31. // watched is a data structure representing watched file/directory.
  32. type watched struct {
  33. trgWatched
  34. // fd is a file descriptor for watched file/directory.
  35. fd int
  36. }
  37. // Stop implements trigger.
  38. func (k *kq) Stop() (err error) {
  39. // trigger event used to interrupt Kevent call.
  40. _, err = syscall.Write(k.pipefds[1], []byte{0x00})
  41. return
  42. }
  43. // Close implements trigger.
  44. func (k *kq) Close() error {
  45. return syscall.Close(k.fd)
  46. }
  47. // NewWatched implements trigger.
  48. func (*kq) NewWatched(p string, fi os.FileInfo) (*watched, error) {
  49. fd, err := syscall.Open(p, syscall.O_NONBLOCK|syscall.O_RDONLY, 0)
  50. if err != nil {
  51. return nil, err
  52. }
  53. return &watched{
  54. trgWatched: trgWatched{p: p, fi: fi},
  55. fd: fd,
  56. }, nil
  57. }
  58. // Record implements trigger.
  59. func (k *kq) Record(w *watched) {
  60. k.idLkp[w.fd], k.pthLkp[w.p] = w, w
  61. }
  62. // Del implements trigger.
  63. func (k *kq) Del(w *watched) {
  64. syscall.Close(w.fd)
  65. delete(k.idLkp, w.fd)
  66. delete(k.pthLkp, w.p)
  67. }
  68. func inter2kq(n interface{}) syscall.Kevent_t {
  69. kq, ok := n.(syscall.Kevent_t)
  70. if !ok {
  71. panic(fmt.Sprintf("kqueue: type should be Kevent_t, %T instead", n))
  72. }
  73. return kq
  74. }
  75. // Init implements trigger.
  76. func (k *kq) Init() (err error) {
  77. if k.fd, err = syscall.Kqueue(); err != nil {
  78. return
  79. }
  80. // Creates pipe used to stop `Kevent` call by registering it,
  81. // watching read end and writing to other end of it.
  82. if err = syscall.Pipe(k.pipefds[:]); err != nil {
  83. return nonil(err, k.Close())
  84. }
  85. var kevn [1]syscall.Kevent_t
  86. syscall.SetKevent(&kevn[0], k.pipefds[0], syscall.EVFILT_READ, syscall.EV_ADD)
  87. if _, err = syscall.Kevent(k.fd, kevn[:], nil, nil); err != nil {
  88. return nonil(err, k.Close())
  89. }
  90. return
  91. }
  92. // Unwatch implements trigger.
  93. func (k *kq) Unwatch(w *watched) (err error) {
  94. var kevn [1]syscall.Kevent_t
  95. syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE, syscall.EV_DELETE)
  96. _, err = syscall.Kevent(k.fd, kevn[:], nil, nil)
  97. return
  98. }
  99. // Watch implements trigger.
  100. func (k *kq) Watch(fi os.FileInfo, w *watched, e int64) (err error) {
  101. var kevn [1]syscall.Kevent_t
  102. syscall.SetKevent(&kevn[0], w.fd, syscall.EVFILT_VNODE,
  103. syscall.EV_ADD|syscall.EV_CLEAR)
  104. kevn[0].Fflags = uint32(e)
  105. _, err = syscall.Kevent(k.fd, kevn[:], nil, nil)
  106. return
  107. }
  108. // Wait implements trigger.
  109. func (k *kq) Wait() (interface{}, error) {
  110. var (
  111. kevn [1]syscall.Kevent_t
  112. err error
  113. )
  114. kevn[0] = syscall.Kevent_t{}
  115. _, err = syscall.Kevent(k.fd, nil, kevn[:], nil)
  116. return kevn[0], err
  117. }
  118. // Watched implements trigger.
  119. func (k *kq) Watched(n interface{}) (*watched, int64, error) {
  120. kevn, ok := n.(syscall.Kevent_t)
  121. if !ok {
  122. panic(fmt.Sprintf("kq: type should be syscall.Kevent_t, %T instead", kevn))
  123. }
  124. if _, ok = k.idLkp[int(kevn.Ident)]; !ok {
  125. return nil, 0, errNotWatched
  126. }
  127. return k.idLkp[int(kevn.Ident)], int64(kevn.Fflags), nil
  128. }
  129. // IsStop implements trigger.
  130. func (k *kq) IsStop(n interface{}, err error) bool {
  131. return int(inter2kq(n).Ident) == k.pipefds[0]
  132. }
  133. func init() {
  134. encode = func(e Event, dir bool) (o int64) {
  135. // Create event is not supported by kqueue. Instead NoteWrite event will
  136. // be registered for a directory. If this event will be reported on dir
  137. // which is to be monitored for Create, dir will be rescanned
  138. // and Create events will be generated and returned for new files.
  139. // In case of files, if not requested NoteRename event is reported,
  140. // it will be ignored.
  141. o = int64(e &^ Create)
  142. if (e&Create != 0 && dir) || e&Write != 0 {
  143. o = (o &^ int64(Write)) | int64(NoteWrite)
  144. }
  145. if e&Rename != 0 {
  146. o = (o &^ int64(Rename)) | int64(NoteRename)
  147. }
  148. if e&Remove != 0 {
  149. o = (o &^ int64(Remove)) | int64(NoteDelete)
  150. }
  151. return
  152. }
  153. nat2not = map[Event]Event{
  154. NoteWrite: Write,
  155. NoteRename: Rename,
  156. NoteDelete: Remove,
  157. NoteExtend: Event(0),
  158. NoteAttrib: Event(0),
  159. NoteRevoke: Event(0),
  160. NoteLink: Event(0),
  161. }
  162. not2nat = map[Event]Event{
  163. Write: NoteWrite,
  164. Rename: NoteRename,
  165. Remove: NoteDelete,
  166. }
  167. }