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  }