psnotify_bsd.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // Copyright (c) 2012 VMware, Inc.
  2. // +build darwin freebsd netbsd openbsd
  3. // Go interface to BSD kqueue process events.
  4. package psnotify
  5. import (
  6. "syscall"
  7. )
  8. const (
  9. // Flags (from <sys/event.h>)
  10. PROC_EVENT_FORK = syscall.NOTE_FORK // fork() events
  11. PROC_EVENT_EXEC = syscall.NOTE_EXEC // exec() events
  12. PROC_EVENT_EXIT = syscall.NOTE_EXIT // exit() events
  13. // Watch for all process events
  14. PROC_EVENT_ALL = PROC_EVENT_FORK | PROC_EVENT_EXEC | PROC_EVENT_EXIT
  15. )
  16. type kqueueListener struct {
  17. kq int // The syscall.Kqueue() file descriptor
  18. buf [1]syscall.Kevent_t // An event buffer for Add/Remove watch
  19. }
  20. // Initialize bsd implementation of the eventListener interface
  21. func createListener() (eventListener, error) {
  22. listener := &kqueueListener{}
  23. kq, err := syscall.Kqueue()
  24. listener.kq = kq
  25. return listener, err
  26. }
  27. // Initialize Kevent_t fields and propagate changelist for the given pid
  28. func (w *Watcher) kevent(pid int, fflags uint32, flags int) error {
  29. listener, _ := w.listener.(*kqueueListener)
  30. event := &listener.buf[0]
  31. syscall.SetKevent(event, pid, syscall.EVFILT_PROC, flags)
  32. event.Fflags = fflags
  33. _, err := syscall.Kevent(listener.kq, listener.buf[:], nil, nil)
  34. return err
  35. }
  36. // Delete filter for given pid from the queue
  37. func (w *Watcher) unregister(pid int) error {
  38. return w.kevent(pid, 0, syscall.EV_DELETE)
  39. }
  40. // Add and enable filter for given pid in the queue
  41. func (w *Watcher) register(pid int, flags uint32) error {
  42. return w.kevent(pid, flags, syscall.EV_ADD|syscall.EV_ENABLE)
  43. }
  44. // Poll the kqueue file descriptor and dispatch to the Event channels
  45. func (w *Watcher) readEvents() {
  46. listener, _ := w.listener.(*kqueueListener)
  47. events := make([]syscall.Kevent_t, 10)
  48. for {
  49. if w.isDone() {
  50. return
  51. }
  52. n, err := syscall.Kevent(listener.kq, nil, events, nil)
  53. if err != nil {
  54. w.Error <- err
  55. continue
  56. }
  57. for _, ev := range events[:n] {
  58. pid := int(ev.Ident)
  59. switch ev.Fflags {
  60. case syscall.NOTE_FORK:
  61. w.Fork <- &ProcEventFork{ParentPid: pid}
  62. case syscall.NOTE_EXEC:
  63. w.Exec <- &ProcEventExec{Pid: pid}
  64. case syscall.NOTE_EXIT:
  65. w.RemoveWatch(pid)
  66. w.Exit <- &ProcEventExit{Pid: pid}
  67. }
  68. }
  69. }
  70. }
  71. // Close our kqueue file descriptor; deletes any remaining filters
  72. func (listener *kqueueListener) close() error {
  73. return syscall.Close(listener.kq)
  74. }