platform_system_core/init/reboot_test.cpp
Nikita Ioffe 0a0e4793e3 Only run RebootTest under root
This test requires running test services, which causes test to crash
(and still incorrectly be reported as passing) when running on
non-rooted device.

Ignore-AOSP-First: reboot_test is not in AOSP yet
Bug: 190958734
Test: atest CtsInitTestCases
Merged-In: I3c5c9917d0a787d66272ccf4aefc57e6573841bc
Change-Id: I3c5c9917d0a787d66272ccf4aefc57e6573841bc
2021-09-29 21:34:59 +09:00

196 lines
6.2 KiB
C++

/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "reboot.h"
#include <errno.h>
#include <unistd.h>
#include <memory>
#include <string_view>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <gtest/gtest.h>
#include <selinux/selinux.h>
#include "builtin_arguments.h"
#include "builtins.h"
#include "parser.h"
#include "service_list.h"
#include "service_parser.h"
#include "subcontext.h"
#include "util.h"
using namespace std::literals;
using android::base::GetProperty;
using android::base::Join;
using android::base::SetProperty;
using android::base::Split;
using android::base::StringReplace;
using android::base::WaitForProperty;
using android::base::WriteStringToFd;
namespace android {
namespace init {
class RebootTest : public ::testing::Test {
public:
RebootTest() {
std::vector<std::string> names = GetServiceNames();
if (!names.empty()) {
ADD_FAILURE() << "Expected empty ServiceList but found: [" << Join(names, ',') << "]";
}
}
~RebootTest() {
std::vector<std::string> names = GetServiceNames();
for (const auto& name : names) {
auto s = ServiceList::GetInstance().FindService(name);
auto pid = s->pid();
ServiceList::GetInstance().RemoveService(*s);
if (pid > 0) {
kill(pid, SIGTERM);
kill(pid, SIGKILL);
}
}
}
private:
std::vector<std::string> GetServiceNames() const {
std::vector<std::string> names;
for (const auto& s : ServiceList::GetInstance()) {
names.push_back(s->name());
}
return names;
}
};
std::string GetSecurityContext() {
char* ctx;
if (getcon(&ctx) == -1) {
ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
}
std::string result = std::string(ctx);
freecon(ctx);
return result;
}
void AddTestService(const std::string& name) {
static constexpr std::string_view kScriptTemplate = R"init(
service $name /system/bin/yes
user shell
group shell
seclabel $selabel
)init";
std::string script = StringReplace(StringReplace(kScriptTemplate, "$name", name, false),
"$selabel", GetSecurityContext(), false);
ServiceList& service_list = ServiceList::GetInstance();
Parser parser;
parser.AddSectionParser("service",
std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(WriteStringToFd(script, tf.fd));
ASSERT_TRUE(parser.ParseConfig(tf.path));
}
TEST_F(RebootTest, StopServicesSIGTERM) {
if (getuid() != 0) {
GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
AddTestService("A");
AddTestService("B");
auto service_a = ServiceList::GetInstance().FindService("A");
ASSERT_NE(nullptr, service_a);
auto service_b = ServiceList::GetInstance().FindService("B");
ASSERT_NE(nullptr, service_b);
ASSERT_RESULT_OK(service_a->Start());
ASSERT_TRUE(service_a->IsRunning());
ASSERT_RESULT_OK(service_b->Start());
ASSERT_TRUE(service_b->IsRunning());
std::unique_ptr<Service> oneshot_service;
{
auto result = Service::MakeTemporaryOneshotService(
{"exec", GetSecurityContext(), "--", "/system/bin/yes"});
ASSERT_RESULT_OK(result);
oneshot_service = std::move(*result);
}
std::string oneshot_service_name = oneshot_service->name();
oneshot_service->Start();
ASSERT_TRUE(oneshot_service->IsRunning());
ServiceList::GetInstance().AddService(std::move(oneshot_service));
EXPECT_EQ(0, StopServicesAndLogViolations({"A", "B", oneshot_service_name}, 10s,
/* terminate= */ true));
EXPECT_FALSE(service_a->IsRunning());
EXPECT_FALSE(service_b->IsRunning());
// Oneshot services are deleted from the ServiceList after they are destroyed.
auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);
EXPECT_EQ(nullptr, oneshot_service_after_stop);
}
TEST_F(RebootTest, StopServicesSIGKILL) {
if (getuid() != 0) {
GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
AddTestService("A");
AddTestService("B");
auto service_a = ServiceList::GetInstance().FindService("A");
ASSERT_NE(nullptr, service_a);
auto service_b = ServiceList::GetInstance().FindService("B");
ASSERT_NE(nullptr, service_b);
ASSERT_RESULT_OK(service_a->Start());
ASSERT_TRUE(service_a->IsRunning());
ASSERT_RESULT_OK(service_b->Start());
ASSERT_TRUE(service_b->IsRunning());
std::unique_ptr<Service> oneshot_service;
{
auto result = Service::MakeTemporaryOneshotService(
{"exec", GetSecurityContext(), "--", "/system/bin/yes"});
ASSERT_RESULT_OK(result);
oneshot_service = std::move(*result);
}
std::string oneshot_service_name = oneshot_service->name();
oneshot_service->Start();
ASSERT_TRUE(oneshot_service->IsRunning());
ServiceList::GetInstance().AddService(std::move(oneshot_service));
EXPECT_EQ(0, StopServicesAndLogViolations({"A", "B", oneshot_service_name}, 10s,
/* terminate= */ false));
EXPECT_FALSE(service_a->IsRunning());
EXPECT_FALSE(service_b->IsRunning());
// Oneshot services are deleted from the ServiceList after they are destroyed.
auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);
EXPECT_EQ(nullptr, oneshot_service_after_stop);
}
} // namespace init
} // namespace android