Skip to content

Commit 660ffde

Browse files
committed
Add reboot_test
This test spawns several services backed by /system/bin/yes executable, and then stops them either while SIGTERM or SIGKILL. Ideally we want to unit test more of reboot logic, but that requires a bigger refactoring. Test: atest CtsInitTestCases Bug: 170315126 Bug: 174335499 Change-Id: Ife48b1636c6ca2d0aac73f4eb6f4737343a88e7a
1 parent fedfd39 commit 660ffde

File tree

5 files changed

+198
-2
lines changed

5 files changed

+198
-2
lines changed

init/Android.bp

+1
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ cc_test {
261261
"persistent_properties_test.cpp",
262262
"property_service_test.cpp",
263263
"property_type_test.cpp",
264+
"reboot_test.cpp",
264265
"rlimit_parser_test.cpp",
265266
"service_test.cpp",
266267
"subcontext_test.cpp",

init/reboot.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -533,8 +533,8 @@ static void StopServices(const std::set<std::string>& services, std::chrono::mil
533533

534534
// Like StopServices, but also logs all the services that failed to stop after the provided timeout.
535535
// Returns number of violators.
536-
static int StopServicesAndLogViolations(const std::set<std::string>& services,
537-
std::chrono::milliseconds timeout, bool terminate) {
536+
int StopServicesAndLogViolations(const std::set<std::string>& services,
537+
std::chrono::milliseconds timeout, bool terminate) {
538538
StopServices(services, timeout, terminate);
539539
int still_running = 0;
540540
for (const auto& s : ServiceList::GetInstance()) {

init/reboot.h

+6
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,17 @@
1717
#ifndef _INIT_REBOOT_H
1818
#define _INIT_REBOOT_H
1919

20+
#include <chrono>
21+
#include <set>
2022
#include <string>
2123

2224
namespace android {
2325
namespace init {
2426

27+
// Like StopServices, but also logs all the services that failed to stop after the provided timeout.
28+
// Returns number of violators.
29+
int StopServicesAndLogViolations(const std::set<std::string>& services,
30+
std::chrono::milliseconds timeout, bool terminate);
2531
// Parses and handles a setprop sys.powerctl message.
2632
void HandlePowerctlMessage(const std::string& command);
2733

init/reboot_test.cpp

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/*
2+
* Copyright (C) 2020 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "reboot.h"
18+
19+
#include <errno.h>
20+
#include <unistd.h>
21+
22+
#include <memory>
23+
#include <string_view>
24+
25+
#include <android-base/file.h>
26+
#include <android-base/properties.h>
27+
#include <android-base/strings.h>
28+
#include <gtest/gtest.h>
29+
#include <selinux/selinux.h>
30+
31+
#include "builtin_arguments.h"
32+
#include "builtins.h"
33+
#include "parser.h"
34+
#include "service_list.h"
35+
#include "service_parser.h"
36+
#include "subcontext.h"
37+
#include "util.h"
38+
39+
using namespace std::literals;
40+
41+
using android::base::GetProperty;
42+
using android::base::Join;
43+
using android::base::SetProperty;
44+
using android::base::Split;
45+
using android::base::StringReplace;
46+
using android::base::WaitForProperty;
47+
using android::base::WriteStringToFd;
48+
49+
namespace android {
50+
namespace init {
51+
52+
class RebootTest : public ::testing::Test {
53+
public:
54+
RebootTest() {
55+
std::vector<std::string> names = GetServiceNames();
56+
if (!names.empty()) {
57+
ADD_FAILURE() << "Expected empty ServiceList but found: [" << Join(names, ',') << "]";
58+
}
59+
}
60+
61+
~RebootTest() {
62+
std::vector<std::string> names = GetServiceNames();
63+
for (const auto& name : names) {
64+
auto s = ServiceList::GetInstance().FindService(name);
65+
auto pid = s->pid();
66+
ServiceList::GetInstance().RemoveService(*s);
67+
if (pid > 0) {
68+
kill(pid, SIGTERM);
69+
kill(pid, SIGKILL);
70+
}
71+
}
72+
}
73+
74+
private:
75+
std::vector<std::string> GetServiceNames() const {
76+
std::vector<std::string> names;
77+
for (const auto& s : ServiceList::GetInstance()) {
78+
names.push_back(s->name());
79+
}
80+
return names;
81+
}
82+
};
83+
84+
std::string GetSecurityContext() {
85+
char* ctx;
86+
if (getcon(&ctx) == -1) {
87+
ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
88+
}
89+
std::string result = std::string(ctx);
90+
freecon(ctx);
91+
return result;
92+
}
93+
94+
void AddTestService(const std::string& name) {
95+
static constexpr std::string_view kScriptTemplate = R"init(
96+
service $name /system/bin/yes
97+
user shell
98+
group shell
99+
seclabel $selabel
100+
)init";
101+
102+
std::string script = StringReplace(StringReplace(kScriptTemplate, "$name", name, false),
103+
"$selabel", GetSecurityContext(), false);
104+
ServiceList& service_list = ServiceList::GetInstance();
105+
Parser parser;
106+
parser.AddSectionParser("service",
107+
std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
108+
109+
TemporaryFile tf;
110+
ASSERT_TRUE(tf.fd != -1);
111+
ASSERT_TRUE(WriteStringToFd(script, tf.fd));
112+
ASSERT_TRUE(parser.ParseConfig(tf.path));
113+
}
114+
115+
TEST_F(RebootTest, StopServicesSIGTERM) {
116+
AddTestService("A");
117+
AddTestService("B");
118+
119+
auto service_a = ServiceList::GetInstance().FindService("A");
120+
ASSERT_NE(nullptr, service_a);
121+
auto service_b = ServiceList::GetInstance().FindService("B");
122+
ASSERT_NE(nullptr, service_b);
123+
124+
ASSERT_RESULT_OK(service_a->Start());
125+
ASSERT_TRUE(service_a->IsRunning());
126+
ASSERT_RESULT_OK(service_b->Start());
127+
ASSERT_TRUE(service_b->IsRunning());
128+
129+
std::unique_ptr<Service> oneshot_service;
130+
{
131+
auto result = Service::MakeTemporaryOneshotService(
132+
{"exec", GetSecurityContext(), "--", "/system/bin/yes"});
133+
ASSERT_RESULT_OK(result);
134+
oneshot_service = std::move(*result);
135+
}
136+
std::string oneshot_service_name = oneshot_service->name();
137+
oneshot_service->Start();
138+
ASSERT_TRUE(oneshot_service->IsRunning());
139+
ServiceList::GetInstance().AddService(std::move(oneshot_service));
140+
141+
EXPECT_EQ(0, StopServicesAndLogViolations({"A", "B", oneshot_service_name}, 10s,
142+
/* terminate= */ true));
143+
EXPECT_FALSE(service_a->IsRunning());
144+
EXPECT_FALSE(service_b->IsRunning());
145+
// Oneshot services are deleted from the ServiceList after they are destroyed.
146+
auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);
147+
EXPECT_EQ(nullptr, oneshot_service_after_stop);
148+
}
149+
150+
TEST_F(RebootTest, StopServicesSIGKILL) {
151+
AddTestService("A");
152+
AddTestService("B");
153+
154+
auto service_a = ServiceList::GetInstance().FindService("A");
155+
ASSERT_NE(nullptr, service_a);
156+
auto service_b = ServiceList::GetInstance().FindService("B");
157+
ASSERT_NE(nullptr, service_b);
158+
159+
ASSERT_RESULT_OK(service_a->Start());
160+
ASSERT_TRUE(service_a->IsRunning());
161+
ASSERT_RESULT_OK(service_b->Start());
162+
ASSERT_TRUE(service_b->IsRunning());
163+
164+
std::unique_ptr<Service> oneshot_service;
165+
{
166+
auto result = Service::MakeTemporaryOneshotService(
167+
{"exec", GetSecurityContext(), "--", "/system/bin/yes"});
168+
ASSERT_RESULT_OK(result);
169+
oneshot_service = std::move(*result);
170+
}
171+
std::string oneshot_service_name = oneshot_service->name();
172+
oneshot_service->Start();
173+
ASSERT_TRUE(oneshot_service->IsRunning());
174+
ServiceList::GetInstance().AddService(std::move(oneshot_service));
175+
176+
EXPECT_EQ(0, StopServicesAndLogViolations({"A", "B", oneshot_service_name}, 10s,
177+
/* terminate= */ false));
178+
EXPECT_FALSE(service_a->IsRunning());
179+
EXPECT_FALSE(service_b->IsRunning());
180+
// Oneshot services are deleted from the ServiceList after they are destroyed.
181+
auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);
182+
EXPECT_EQ(nullptr, oneshot_service_after_stop);
183+
}
184+
185+
} // namespace init
186+
} // namespace android

init/subcontext.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,9 @@ Subcontext* GetSubcontext() {
351351
}
352352

353353
bool SubcontextChildReap(pid_t pid) {
354+
if (!subcontext) {
355+
return false;
356+
}
354357
if (subcontext->pid() == pid) {
355358
if (!subcontext_terminated_by_shutdown) {
356359
subcontext->Restart();

0 commit comments

Comments
 (0)