| // Copyright 2015 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "germ/container_manager.h" |
| |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| #include <string> |
| |
| #include <base/at_exit.h> |
| #include <base/run_loop.h> |
| #include <base/time/time.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "germ/container.h" |
| #include "germ/mock_germ_zygote.h" |
| #include "germ/test_util.h" |
| |
| using ::testing::_; |
| using ::testing::InSequence; |
| using ::testing::Return; |
| using ::testing::SetArgPointee; |
| |
| namespace germ { |
| namespace { |
| |
| // When killing a container, a delayed task is queued up which sends SIGKILL to |
| // the container in case it does not die in response to SIGTERM. This test |
| // ensures that SIGKILL is never sent to a newer instance of the container. |
| TEST(ContainerTest, OnlyKillCurrentGeneration) { |
| base::AtExitManager exit_manager; |
| base::MessageLoopForIO message_loop; |
| |
| MockGermZygote mock_zygote; |
| |
| const char kContainerName[] = "test_container"; |
| const pid_t kContainerPid = 1234; |
| const pid_t kRestartedContainerPid = 5678; |
| |
| soma::ContainerSpec spec = MakeSpecForTest(kContainerName); |
| |
| { |
| InSequence seq; |
| EXPECT_CALL(mock_zygote, StartContainer(EqualsSpec(spec), _)) |
| .WillOnce(DoAll(SetArgPointee<1>(kContainerPid), Return(true))); |
| EXPECT_CALL(mock_zygote, Kill(kContainerPid, SIGTERM)) |
| .WillOnce(Return(true)); |
| |
| EXPECT_CALL(mock_zygote, StartContainer(EqualsSpec(spec), _)) |
| .WillOnce( |
| DoAll(SetArgPointee<1>(kRestartedContainerPid), Return(true))); |
| EXPECT_CALL(mock_zygote, Kill(kRestartedContainerPid, SIGTERM)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_zygote, Kill(kRestartedContainerPid, SIGKILL)) |
| .WillOnce(Return(true)); |
| } |
| |
| // Start a container, and terminate it by sending it a SIGTERM (and queuing up |
| // a SIGKILL for later). |
| scoped_refptr<Container> container = new Container(spec); |
| EXPECT_TRUE(container->Launch(&mock_zygote)); |
| EXPECT_EQ(kContainerPid, container->init_pid()); |
| EXPECT_EQ(Container::State::RUNNING, container->state()); |
| |
| EXPECT_TRUE(container->Terminate(&mock_zygote, base::TimeDelta())); |
| EXPECT_EQ(Container::State::DYING, container->state()); |
| |
| // Pretend that the container was killed by the SIGTERM. |
| container->OnReap(); |
| EXPECT_EQ(Container::State::STOPPED, container->state()); |
| |
| // Start the container again then terminate it. |
| EXPECT_TRUE(container->Launch(&mock_zygote)); |
| EXPECT_EQ(kRestartedContainerPid, container->init_pid()); |
| EXPECT_EQ(Container::State::RUNNING, container->state()); |
| |
| EXPECT_TRUE(container->Terminate(&mock_zygote, base::TimeDelta())); |
| EXPECT_EQ(Container::State::DYING, container->state()); |
| |
| // Now, the message loop should have the SIGKILL tasks for both instantiations |
| // of the container, but only the one for the second instantiation should |
| // do anything. |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| } |
| |
| // When killing a container, a delayed task is queued up which sends SIGKILL to |
| // the container in case it does not die in response to SIGTERM. This test |
| // ensures that SIGKILL is not sent if the process dies in response to SIGTERM. |
| TEST(ContainerTest, DoesNotSendSIGKILLToReapedContainer) { |
| base::AtExitManager exit_manager; |
| base::MessageLoopForIO message_loop; |
| |
| MockGermZygote mock_zygote; |
| |
| const char kContainerName[] = "test_container"; |
| const pid_t kContainerPid = 1234; |
| |
| soma::ContainerSpec spec = MakeSpecForTest(kContainerName); |
| |
| { |
| InSequence seq; |
| EXPECT_CALL(mock_zygote, StartContainer(EqualsSpec(spec), _)) |
| .WillOnce(DoAll(SetArgPointee<1>(kContainerPid), Return(true))); |
| EXPECT_CALL(mock_zygote, Kill(kContainerPid, SIGTERM)) |
| .WillOnce(Return(true)); |
| } |
| |
| // Start a container, and terminate it by sending it a SIGTERM (and queuing up |
| // a SIGKILL for later). |
| scoped_refptr<Container> container = new Container(spec); |
| EXPECT_TRUE(container->Launch(&mock_zygote)); |
| EXPECT_EQ(Container::State::RUNNING, container->state()); |
| EXPECT_EQ(kContainerPid, container->init_pid()); |
| |
| EXPECT_TRUE(container->Terminate(&mock_zygote, base::TimeDelta())); |
| EXPECT_EQ(Container::State::DYING, container->state()); |
| |
| // Pretend that the container was killed by the SIGTERM. |
| container->OnReap(); |
| EXPECT_EQ(Container::State::STOPPED, container->state()); |
| |
| // Now, the message loop should have the SIGKILL task, but it should do |
| // nothing because the container is already in state STOPPED. |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| } |
| |
| } // namespace |
| } // namespace germ |