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

     1  // Copyright (c) 2012 VMware, Inc.
     2  
     3  // +build darwin freebsd netbsd openbsd linux
     4  
     5  package psnotify
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  )
    11  
    12  type ProcEventFork struct {
    13  	ParentPid int // Pid of the process that called fork()
    14  	ChildPid  int // Child process pid created by fork()
    15  }
    16  
    17  type ProcEventExec struct {
    18  	Pid int // Pid of the process that called exec()
    19  }
    20  
    21  type ProcEventExit struct {
    22  	Pid int // Pid of the process that called exit()
    23  }
    24  
    25  type watch struct {
    26  	flags uint32 // Saved value of Watch() flags param
    27  }
    28  
    29  type eventListener interface {
    30  	close() error // Watch.Close() closes the OS specific listener
    31  }
    32  
    33  type Watcher struct {
    34  	listener eventListener       // OS specifics (kqueue or netlink)
    35  	watches  map[int]*watch      // Map of watched process ids
    36  	Error    chan error          // Errors are sent on this channel
    37  	Fork     chan *ProcEventFork // Fork events are sent on this channel
    38  	Exec     chan *ProcEventExec // Exec events are sent on this channel
    39  	Exit     chan *ProcEventExit // Exit events are sent on this channel
    40  	done     chan bool           // Used to stop the readEvents() goroutine
    41  	isClosed bool                // Set to true when Close() is first called
    42  }
    43  
    44  // Initialize event listener and channels
    45  func NewWatcher() (*Watcher, error) {
    46  	listener, err := createListener()
    47  
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	w := &Watcher{
    53  		listener: listener,
    54  		watches:  make(map[int]*watch),
    55  		Fork:     make(chan *ProcEventFork),
    56  		Exec:     make(chan *ProcEventExec),
    57  		Exit:     make(chan *ProcEventExit),
    58  		Error:    make(chan error),
    59  		done:     make(chan bool, 1),
    60  	}
    61  
    62  	go w.readEvents()
    63  	return w, nil
    64  }
    65  
    66  // Close event channels when done message is received
    67  func (w *Watcher) finish() {
    68  	close(w.Fork)
    69  	close(w.Exec)
    70  	close(w.Exit)
    71  	close(w.Error)
    72  }
    73  
    74  // Closes the OS specific event listener,
    75  // removes all watches and closes all event channels.
    76  func (w *Watcher) Close() error {
    77  	if w.isClosed {
    78  		return nil
    79  	}
    80  	w.isClosed = true
    81  
    82  	for pid := range w.watches {
    83  		w.RemoveWatch(pid)
    84  	}
    85  
    86  	w.done <- true
    87  
    88  	w.listener.close()
    89  
    90  	return nil
    91  }
    92  
    93  // Add pid to the watched process set.
    94  // The flags param is a bitmask of process events to capture,
    95  // must be one or more of: PROC_EVENT_FORK, PROC_EVENT_EXEC, PROC_EVENT_EXIT
    96  func (w *Watcher) Watch(pid int, flags uint32) error {
    97  	if w.isClosed {
    98  		return errors.New("psnotify watcher is closed")
    99  	}
   100  
   101  	watchEntry, found := w.watches[pid]
   102  
   103  	if found {
   104  		watchEntry.flags |= flags
   105  	} else {
   106  		if err := w.register(pid, flags); err != nil {
   107  			return err
   108  		}
   109  		w.watches[pid] = &watch{flags: flags}
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  // Remove pid from the watched process set.
   116  func (w *Watcher) RemoveWatch(pid int) error {
   117  	_, ok := w.watches[pid]
   118  	if !ok {
   119  		msg := fmt.Sprintf("watch for pid=%d does not exist", pid)
   120  		return errors.New(msg)
   121  	}
   122  	delete(w.watches, pid)
   123  	return w.unregister(pid)
   124  }
   125  
   126  // Internal helper to check if there is a message on the "done" channel.
   127  // The "done" message is sent by the Close() method; when received here,
   128  // the Watcher.finish method is called to close all channels and return
   129  // true - in which case the caller should break from the readEvents loop.
   130  func (w *Watcher) isDone() bool {
   131  	var done bool
   132  	select {
   133  	case done = <-w.done:
   134  		w.finish()
   135  	default:
   136  	}
   137  	return done
   138  }