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 [](decltype(this) test, const siginfo_t& info) { 79 EXPECT_EQ(CLD_EXITED, info.si_code); 80 EXPECT_EQ(123, info.si_status); 81 test->brillo_loop_.BreakLoop(); 82 }, base::Unretained(this)))); 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( 95 FROM_HERE, 96 pid, 97 base::Bind( 98 [](decltype(this) test, 99 int exit_value, 100 int* running_childs, 101 const siginfo_t& info) { 102 EXPECT_EQ(CLD_EXITED, info.si_code); 103 EXPECT_EQ(exit_value, info.si_status); 104 (*running_childs)--; 105 if (*running_childs == 0) 106 test->brillo_loop_.BreakLoop(); 107 }, 108 base::Unretained(this), 109 exit_value, 110 base::Unretained(&running_childs)))); 111 } 112 // This sleep is optional. It helps to have more processes exit before we 113 // start watching for them in the message loop. 114 usleep(10 * 1000); 115 brillo_loop_.Run(); 116 EXPECT_EQ(0, running_childs); 117} 118 119TEST_F(ProcessReaperTest, ReapKilledChild) { 120 pid_t pid = ForkChildAndKill(SIGKILL); 121 EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind( 122 [](decltype(this) test, const siginfo_t& info) { 123 EXPECT_EQ(CLD_KILLED, info.si_code); 124 EXPECT_EQ(SIGKILL, info.si_status); 125 test->brillo_loop_.BreakLoop(); 126 }, base::Unretained(this)))); 127 brillo_loop_.Run(); 128} 129 130TEST_F(ProcessReaperTest, ReapKilledAndForgottenChild) { 131 pid_t pid = ForkChildAndExit(0); 132 EXPECT_TRUE(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind( 133 [](decltype(this) test, const siginfo_t& /* info */) { 134 ADD_FAILURE() << "Child process was still tracked."; 135 test->brillo_loop_.BreakLoop(); 136 }, base::Unretained(this)))); 137 EXPECT_TRUE(process_reaper_.ForgetChild(pid)); 138 139 // A second call should return failure. 140 EXPECT_FALSE(process_reaper_.ForgetChild(pid)); 141 142 // Run the loop with a timeout, as the BreakLoop() above is not expected. 143 brillo_loop_.PostDelayedTask(FROM_HERE, 144 base::Bind(&MessageLoop::BreakLoop, 145 base::Unretained(&brillo_loop_)), 146 base::TimeDelta::FromMilliseconds(100)); 147 brillo_loop_.Run(); 148} 149 150} // namespace brillo 151