diff --git a/init/README.md b/init/README.md index 957eb9e4a..4a5d0ec35 100644 --- a/init/README.md +++ b/init/README.md @@ -244,6 +244,10 @@ runs the service. "r", "w" or "rw". For native executables see libcutils android\_get\_control\_file(). +`gentle_kill` +> This service will be sent SIGTERM instead of SIGKILL when stopped. After a 200 ms timeout, it will + be sent SIGKILL. + `group [ \* ]` > Change to 'groupname' before exec'ing this service. Additional groupnames beyond the (required) first one are used to set the diff --git a/init/init_test.cpp b/init/init_test.cpp index 1ab69acb3..1e69ede0b 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -642,6 +643,91 @@ TEST(init, MemLockLimit) { ASSERT_LE(curr_limit.rlim_max, max_limit); } +static std::vector ConvertToArgv(const std::vector& args) { + std::vector argv; + argv.reserve(args.size() + 1); + for (const auto& arg : args) { + if (argv.empty()) { + LOG(DEBUG) << arg; + } else { + LOG(DEBUG) << " " << arg; + } + argv.emplace_back(arg.data()); + } + argv.emplace_back(nullptr); + return argv; +} + +pid_t ForkExecvpAsync(const std::vector& args) { + auto argv = ConvertToArgv(args); + + pid_t pid = fork(); + if (pid == 0) { + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + execvp(argv[0], const_cast(argv.data())); + PLOG(ERROR) << "exec in ForkExecvpAsync init test"; + _exit(EXIT_FAILURE); + } + if (pid == -1) { + PLOG(ERROR) << "fork in ForkExecvpAsync init test"; + return -1; + } + return pid; +} + +TEST(init, GentleKill) { + std::string init_script = R"init( +service test_gentle_kill /system/bin/sleep 1000 + disabled + oneshot + gentle_kill + user root + group root + seclabel u:r:toolbox:s0 +)init"; + + ActionManager action_manager; + ServiceList service_list; + TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list); + ASSERT_EQ(std::distance(service_list.begin(), service_list.end()), 1); + + auto service = service_list.begin()->get(); + ASSERT_NE(service, nullptr); + ASSERT_RESULT_OK(service->Start()); + const pid_t pid = service->pid(); + ASSERT_GT(pid, 0); + EXPECT_NE(getsid(pid), 0); + + TemporaryFile logfile; + logfile.DoNotRemove(); + ASSERT_TRUE(logfile.fd != -1); + + std::vector cmd; + cmd.push_back("system/bin/strace"); + cmd.push_back("-o"); + cmd.push_back(logfile.path); + cmd.push_back("-e"); + cmd.push_back("signal"); + cmd.push_back("-p"); + cmd.push_back(std::to_string(pid)); + pid_t strace_pid = ForkExecvpAsync(cmd); + + // Give strace a moment to connect + std::this_thread::sleep_for(1s); + service->Stop(); + + int status; + waitpid(strace_pid, &status, 0); + + std::string logs; + android::base::ReadFdToString(logfile.fd, &logs); + int pos = logs.find("killed by SIGTERM"); + ASSERT_NE(pos, (int)std::string::npos); +} + class TestCaseLogger : public ::testing::EmptyTestEventListener { void OnTestStart(const ::testing::TestInfo& test_info) override { #ifdef __ANDROID__ diff --git a/init/service.cpp b/init/service.cpp index b9b33090e..87d9c3a6c 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -887,6 +888,10 @@ void Service::StopOrReset(int how) { } if (pid_) { + if (flags_ & SVC_GENTLE_KILL) { + KillProcessGroup(SIGTERM); + if (!process_cgroup_empty()) std::this_thread::sleep_for(200ms); + } KillProcessGroup(SIGKILL); NotifyStateChange("stopping"); } else { diff --git a/init/service.h b/init/service.h index f9749d207..3ef890299 100644 --- a/init/service.h +++ b/init/service.h @@ -56,6 +56,8 @@ // should not be killed during shutdown #define SVC_TEMPORARY 0x1000 // This service was started by 'exec' and should be removed from the // service list once it is reaped. +#define SVC_GENTLE_KILL 0x2000 // This service should be stopped with SIGTERM instead of SIGKILL + // Will still be SIGKILLed after timeout period of 200 ms #define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups diff --git a/init/service_parser.cpp b/init/service_parser.cpp index 24a202444..356308423 100644 --- a/init/service_parser.cpp +++ b/init/service_parser.cpp @@ -151,6 +151,11 @@ Result ServiceParser::ParseEnterNamespace(std::vector&& args) return {}; } +Result ServiceParser::ParseGentleKill(std::vector&& args) { + service_->flags_ |= SVC_GENTLE_KILL; + return {}; +} + Result ServiceParser::ParseGroup(std::vector&& args) { auto gid = DecodeUid(args[1]); if (!gid.ok()) { @@ -584,6 +589,7 @@ const KeywordMap& ServiceParser::GetParserMap() con {"disabled", {0, 0, &ServiceParser::ParseDisabled}}, {"enter_namespace", {2, 2, &ServiceParser::ParseEnterNamespace}}, {"file", {2, 2, &ServiceParser::ParseFile}}, + {"gentle_kill", {0, 0, &ServiceParser::ParseGentleKill}}, {"group", {1, NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}}, {"interface", {2, 2, &ServiceParser::ParseInterface}}, {"ioprio", {2, 2, &ServiceParser::ParseIoprio}}, diff --git a/init/service_parser.h b/init/service_parser.h index 54503dd1c..670a5c6cd 100644 --- a/init/service_parser.h +++ b/init/service_parser.h @@ -53,6 +53,7 @@ class ServiceParser : public SectionParser { Result ParseDisabled(std::vector&& args); Result ParseEnterNamespace(std::vector&& args); Result ParseGroup(std::vector&& args); + Result ParseGentleKill(std::vector&& args); Result ParsePriority(std::vector&& args); Result ParseInterface(std::vector&& args); Result ParseIoprio(std::vector&& args);