1// Copyright 2015 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <brillo/process_reaper.h>
6
7#include <signal.h>
8#include <sys/wait.h>
9#include <unistd.h>
10
11#include <base/bind.h>
12#include <base/location.h>
13#include <base/message_loop/message_loop.h>
14#include <brillo/asynchronous_signal_handler.h>
15#include <brillo/bind_lambda.h>
16#include <brillo/message_loops/base_message_loop.h>
17#include <gtest/gtest.h>
18
19namespace {
20
21pid_t ForkChildAndExit(int exit_code) {
22  pid_t pid = fork();
23  PCHECK(pid != -1);
24  if (pid == 0) {
25    _exit(exit_code);
26  }
27  return pid;
28}
29
30pid_t ForkChildAndKill(int sig) {
31  pid_t pid = fork();
32  PCHECK(pid != -1);
33  if (pid == 0) {
34    if (raise(sig) != 0) {
35      PLOG(ERROR) << "raise(" << sig << ")";
36    }
37    _exit(0);  // Not reached. This value will cause the test to fail.
38  }
39  return pid;
40}
41
42}  // namespace
43
44namespace brillo {
45
46class ProcessReaperTest : public ::testing::Test {
47 public:
48  void SetUp() override {
49    brillo_loop_.SetAsCurrent();
50    async_signal_handler_.Init();
51    process_reaper_.Register(&async_signal_handler_);
52  }
53
54 protected:
55  base::MessageLoopForIO base_loop_;
56  brillo::BaseMessageLoop brillo_loop_{&base_loop_};
57  brillo::AsynchronousSignalHandler async_signal_handler_;
58
59  // ProcessReaper under test.
60  ProcessReaper process_reaper_;
61};
62
63TEST_F(ProcessReaperTest, UnregisterWhenNotRegistered) {
64  ProcessReaper another_process_reaper_;
65  another_process_reaper_.Unregister();
66}
67
68TEST_F(ProcessReaperTest, UnregisterAndReregister) {
69  process_reaper_.Unregister();
70  process_reaper_.Register(&async_signal_handler_);
71  // This checks that we can unregister the ProcessReaper and then destroy it.
72  process_reaper_.Unregister();
73}
74
75TEST_F(ProcessReaperTest, ReapExitedChild) {
76  pid_t pid = ForkChildAndExit(123);
77  EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind(
78      [this](const siginfo_t& info) {
79        EXPECT_EQ(CLD_EXITED, info.si_code);
80        EXPECT_EQ(123, info.si_status);
81        this->brillo_loop_.BreakLoop();
82      })));
83  brillo_loop_.Run();
84}
85
86// Test that simultaneous child processes fire their respective callbacks when
87// exiting.
88TEST_F(ProcessReaperTest, ReapedChildsMatchCallbacks) {
89  int running_childs = 10;
90  for (int i = 0; i < running_childs; ++i) {
91    // Different processes will have different exit values.
92    int exit_value = 1 + i;
93    pid_t pid = ForkChildAndExit(exit_value);
94    EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind(
95        [this, exit_value, &running_childs](const siginfo_t& info) {
96          EXPECT_EQ(CLD_EXITED, info.si_code);
97          EXPECT_EQ(exit_value, info.si_status);
98          running_childs--;
99          if (running_childs == 0)
100            this->brillo_loop_.BreakLoop();
101        })));
102  }
103  // This sleep is optional. It helps to have more processes exit before we
104  // start watching for them in the message loop.
105  usleep(10 * 1000);
106  brillo_loop_.Run();
107  EXPECT_EQ(0, running_childs);
108}
109
110TEST_F(ProcessReaperTest, ReapKilledChild) {
111  pid_t pid = ForkChildAndKill(SIGKILL);
112  EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind(
113      [this](const siginfo_t& info) {
114        EXPECT_EQ(CLD_KILLED, info.si_code);
115        EXPECT_EQ(SIGKILL, info.si_status);
116        this->brillo_loop_.BreakLoop();
117      })));
118  brillo_loop_.Run();
119}
120
121TEST_F(ProcessReaperTest, ReapKilledAndForgottenChild) {
122  pid_t pid = ForkChildAndExit(0);
123  EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind(
124      [this](const siginfo_t& /* info */) {
125        ADD_FAILURE() << "Child process was still tracked.";
126        this->brillo_loop_.BreakLoop();
127      })));
128  EXPECT_TRUE(process_reaper_.ForgetChild(pid));
129
130  // A second call should return failure.
131  EXPECT_FALSE(process_reaper_.ForgetChild(pid));
132
133  // Run the loop with a timeout, as the BreakLoop() above is not expected.
134  brillo_loop_.PostDelayedTask(FROM_HERE,
135                               base::Bind(&MessageLoop::BreakLoop,
136                                          base::Unretained(&brillo_loop_)),
137                               base::TimeDelta::FromMilliseconds(100));
138  brillo_loop_.Run();
139}
140
141}  // namespace brillo
142