github.com/elastic/gosigar@v0.14.3/psnotify/psnotify_test.go (about) 1 // Copyright (c) 2012 VMware, Inc. 2 3 // +build darwin freebsd netbsd openbsd linux 4 5 package psnotify 6 7 import ( 8 "fmt" 9 "os" 10 "os/exec" 11 "runtime" 12 "syscall" 13 "testing" 14 "time" 15 ) 16 17 type anyEvent struct { 18 exits []int 19 forks []int 20 execs []int 21 errors []error 22 done chan bool 23 } 24 25 type testWatcher struct { 26 t *testing.T 27 watcher *Watcher 28 events *anyEvent 29 } 30 31 // General purpose Watcher wrapper for all tests 32 func newTestWatcher(t *testing.T) *testWatcher { 33 watcher, err := NewWatcher() 34 if err != nil { 35 t.Fatal(err) 36 } 37 38 events := &anyEvent{ 39 done: make(chan bool, 1), 40 } 41 42 tw := &testWatcher{ 43 t: t, 44 watcher: watcher, 45 events: events, 46 } 47 48 go func() { 49 for { 50 select { 51 case <-events.done: 52 return 53 case ev := <-watcher.Fork: 54 events.forks = append(events.forks, ev.ParentPid) 55 case ev := <-watcher.Exec: 56 events.execs = append(events.execs, ev.Pid) 57 case ev := <-watcher.Exit: 58 events.exits = append(events.exits, ev.Pid) 59 case err := <-watcher.Error: 60 events.errors = append(events.errors, err) 61 } 62 } 63 }() 64 65 return tw 66 } 67 68 func (tw *testWatcher) close() { 69 pause := 100 * time.Millisecond 70 time.Sleep(pause) 71 72 tw.events.done <- true 73 74 tw.watcher.Close() 75 76 time.Sleep(pause) 77 } 78 79 func skipTest(t *testing.T) bool { 80 if runtime.GOOS == "linux" && os.Getuid() != 0 { 81 fmt.Println("SKIP: test must be run as root on linux") 82 return true 83 } 84 return false 85 } 86 87 func startSleepCommand(t *testing.T) *exec.Cmd { 88 cmd := exec.Command("sh", "-c", "sleep 100") 89 if err := cmd.Start(); err != nil { 90 t.Error(err) 91 } 92 return cmd 93 } 94 95 func runCommand(t *testing.T, name string) *exec.Cmd { 96 cmd := exec.Command(name) 97 if err := cmd.Run(); err != nil { 98 t.Error(err) 99 } 100 return cmd 101 } 102 103 func expectEvents(t *testing.T, num int, name string, pids []int) bool { 104 if len(pids) != num { 105 t.Errorf("Expected %d %s events, got=%v", num, name, pids) 106 return false 107 } 108 return true 109 } 110 111 func expectEventPid(t *testing.T, name string, expect int, pid int) bool { 112 if expect != pid { 113 t.Errorf("Expected %s pid=%d, received=%d", name, expect, pid) 114 return false 115 } 116 return true 117 } 118 119 func TestWatchFork(t *testing.T) { 120 if skipTest(t) { 121 return 122 } 123 124 pid := os.Getpid() 125 126 tw := newTestWatcher(t) 127 128 // no watches added yet, so this fork event will no be captured 129 runCommand(t, "date") 130 131 // watch fork events for this process 132 if err := tw.watcher.Watch(pid, PROC_EVENT_FORK); err != nil { 133 t.Error(err) 134 } 135 136 // this fork event will be captured, 137 // the exec and exit events will not be captured 138 runCommand(t, "cal") 139 140 tw.close() 141 142 if expectEvents(t, 1, "forks", tw.events.forks) { 143 expectEventPid(t, "fork", pid, tw.events.forks[0]) 144 } 145 146 expectEvents(t, 0, "execs", tw.events.execs) 147 expectEvents(t, 0, "exits", tw.events.exits) 148 } 149 150 func TestWatchExit(t *testing.T) { 151 if skipTest(t) { 152 return 153 } 154 155 tw := newTestWatcher(t) 156 157 cmd := startSleepCommand(t) 158 159 childPid := cmd.Process.Pid 160 161 // watch for exit event of our child process 162 if err := tw.watcher.Watch(childPid, PROC_EVENT_EXIT); err != nil { 163 t.Error(err) 164 } 165 166 // kill our child process, triggers exit event 167 syscall.Kill(childPid, syscall.SIGTERM) 168 169 cmd.Wait() 170 171 tw.close() 172 173 expectEvents(t, 0, "forks", tw.events.forks) 174 175 expectEvents(t, 0, "execs", tw.events.execs) 176 177 if expectEvents(t, 1, "exits", tw.events.exits) { 178 expectEventPid(t, "exit", childPid, tw.events.exits[0]) 179 } 180 } 181 182 // combined version of TestWatchFork() and TestWatchExit() 183 func TestWatchForkAndExit(t *testing.T) { 184 if skipTest(t) { 185 return 186 } 187 188 pid := os.Getpid() 189 190 tw := newTestWatcher(t) 191 192 if err := tw.watcher.Watch(pid, PROC_EVENT_FORK); err != nil { 193 t.Error(err) 194 } 195 196 cmd := startSleepCommand(t) 197 198 childPid := cmd.Process.Pid 199 200 if err := tw.watcher.Watch(childPid, PROC_EVENT_EXIT); err != nil { 201 t.Error(err) 202 } 203 204 syscall.Kill(childPid, syscall.SIGTERM) 205 206 cmd.Wait() 207 208 tw.close() 209 210 if expectEvents(t, 1, "forks", tw.events.forks) { 211 expectEventPid(t, "fork", pid, tw.events.forks[0]) 212 } 213 214 expectEvents(t, 0, "execs", tw.events.execs) 215 216 if expectEvents(t, 1, "exits", tw.events.exits) { 217 expectEventPid(t, "exit", childPid, tw.events.exits[0]) 218 } 219 } 220 221 func TestWatchFollowFork(t *testing.T) { 222 if skipTest(t) { 223 return 224 } 225 226 // Darwin is not able to follow forks, as the kqueue fork event 227 // does not provide the child pid. 228 if runtime.GOOS != "linux" { 229 fmt.Println("SKIP: test follow forks is linux only") 230 return 231 } 232 233 pid := os.Getpid() 234 235 tw := newTestWatcher(t) 236 237 // watch for all process events related to this process 238 if err := tw.watcher.Watch(pid, PROC_EVENT_ALL); err != nil { 239 t.Error(err) 240 } 241 242 commands := []string{"date", "cal"} 243 childPids := make([]int, len(commands)) 244 245 // triggers fork/exec/exit events for each command 246 for i, name := range commands { 247 cmd := runCommand(t, name) 248 childPids[i] = cmd.Process.Pid 249 } 250 251 // remove watch for this process 252 tw.watcher.RemoveWatch(pid) 253 254 // run commands again to make sure we don't receive any unwanted events 255 for _, name := range commands { 256 runCommand(t, name) 257 } 258 259 tw.close() 260 261 // run commands again to make sure nothing panics after 262 // closing the watcher 263 for _, name := range commands { 264 runCommand(t, name) 265 } 266 267 num := len(commands) 268 if expectEvents(t, num, "forks", tw.events.forks) { 269 for _, epid := range tw.events.forks { 270 expectEventPid(t, "fork", pid, epid) 271 } 272 } 273 274 if expectEvents(t, num, "execs", tw.events.execs) { 275 for i, epid := range tw.events.execs { 276 expectEventPid(t, "exec", childPids[i], epid) 277 } 278 } 279 280 if expectEvents(t, num, "exits", tw.events.exits) { 281 for i, epid := range tw.events.exits { 282 expectEventPid(t, "exit", childPids[i], epid) 283 } 284 } 285 }