github.com/elastic/gosigar@v0.14.3/psnotify/psnotify_bsd.go (about)

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