Home | History | Annotate | Download | only in brillo
      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 
     19 namespace {
     20 
     21 pid_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 
     30 pid_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 
     44 namespace brillo {
     45 
     46 class 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 
     63 TEST_F(ProcessReaperTest, UnregisterWhenNotRegistered) {
     64   ProcessReaper another_process_reaper_;
     65   another_process_reaper_.Unregister();
     66 }
     67 
     68 TEST_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 
     75 TEST_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.
     88 TEST_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 
    110 TEST_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 
    121 TEST_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