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 }