|  | /* | 
|  | * 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 |