Merge "init: allow customizable restart and timeout periods for services" am: 5f2a21d244
am: 63bd73a5cf
Change-Id: I649c79d218f28ddb95c8c1a18f91657d7e1e4ca9
diff --git a/init/README.md b/init/README.md
index b45da21..02a65d5 100644
--- a/init/README.md
+++ b/init/README.md
@@ -262,6 +262,13 @@
> Scheduling priority of the service process. This value has to be in range
-20 to 19. Default priority is 0. Priority is set via setpriority().
+`restart_period <seconds>`
+> If a non-oneshot service exits, it will be restarted at its start time plus
+ this period. It defaults to 5s to rate limit crashing services.
+ This can be increased for services that are meant to run periodically. For
+ example, it may be set to 3600 to indicate that the service should run every hour
+ or 86400 to indicate that the service should run every day.
+
`rlimit <resource> <cur> <max>`
> This applies the given rlimit to the service. rlimits are inherited by child
processes, so this effectively applies the given rlimit to the process tree
@@ -298,6 +305,12 @@
seclabel or computed based on the service executable file security context.
For native executables see libcutils android\_get\_control\_socket().
+`timeout_period <seconds>`
+> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
+ here, so oneshot services do not automatically restart, however all other services will.
+ This is particularly useful for creating a periodic service combined with the restart_period
+ option described above.
+
`user <username>`
> Change to 'username' before exec'ing this service.
Currently defaults to root. (??? probably should default to nobody)
diff --git a/init/init.cpp b/init/init.cpp
index 47cfe32..38bc16e 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -187,23 +187,34 @@
}
}
-static std::optional<boot_clock::time_point> RestartProcesses() {
- std::optional<boot_clock::time_point> next_process_restart_time;
+static std::optional<boot_clock::time_point> HandleProcessActions() {
+ std::optional<boot_clock::time_point> next_process_action_time;
for (const auto& s : ServiceList::GetInstance()) {
+ if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
+ auto timeout_time = s->time_started() + *s->timeout_period();
+ if (boot_clock::now() > timeout_time) {
+ s->Timeout();
+ } else {
+ if (!next_process_action_time || timeout_time < *next_process_action_time) {
+ next_process_action_time = timeout_time;
+ }
+ }
+ }
+
if (!(s->flags() & SVC_RESTARTING)) continue;
- auto restart_time = s->time_started() + 5s;
+ auto restart_time = s->time_started() + s->restart_period();
if (boot_clock::now() > restart_time) {
if (auto result = s->Start(); !result) {
LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
}
} else {
- if (!next_process_restart_time || restart_time < *next_process_restart_time) {
- next_process_restart_time = restart_time;
+ if (!next_process_action_time || restart_time < *next_process_action_time) {
+ next_process_action_time = restart_time;
}
}
}
- return next_process_restart_time;
+ return next_process_action_time;
}
static Result<Success> DoControlStart(Service* service) {
@@ -770,12 +781,12 @@
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
- auto next_process_restart_time = RestartProcesses();
+ auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
- if (next_process_restart_time) {
+ if (next_process_action_time) {
epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
- *next_process_restart_time - boot_clock::now());
+ *next_process_action_time - boot_clock::now());
if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
diff --git a/init/service.cpp b/init/service.cpp
index d20e90a..a3e5953 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -627,6 +627,15 @@
return Success();
}
+Result<Success> Service::ParseRestartPeriod(const std::vector<std::string>& args) {
+ int period;
+ if (!ParseInt(args[1], &period, 5)) {
+ return Error() << "restart_period value must be an integer >= 5";
+ }
+ restart_period_ = std::chrono::seconds(period);
+ return Success();
+}
+
Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
seclabel_ = args[1];
return Success();
@@ -650,6 +659,15 @@
return Error() << "Invalid shutdown option";
}
+Result<Success> Service::ParseTimeoutPeriod(const std::vector<std::string>& args) {
+ int period;
+ if (!ParseInt(args[1], &period, 1)) {
+ return Error() << "timeout_period value must be an integer >= 1";
+ }
+ timeout_period_ = std::chrono::seconds(period);
+ return Success();
+}
+
template <typename T>
Result<Success> Service::AddDescriptor(const std::vector<std::string>& args) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
@@ -757,12 +775,16 @@
{1, 1, &Service::ParseOomScoreAdjust}},
{"override", {0, 0, &Service::ParseOverride}},
{"priority", {1, 1, &Service::ParsePriority}},
+ {"restart_period",
+ {1, 1, &Service::ParseRestartPeriod}},
{"rlimit", {3, 3, &Service::ParseProcessRlimit}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
{"shutdown", {1, 1, &Service::ParseShutdown}},
{"sigstop", {0, 0, &Service::ParseSigstop}},
{"socket", {3, 6, &Service::ParseSocket}},
+ {"timeout_period",
+ {1, 1, &Service::ParseTimeoutPeriod}},
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
@@ -1022,6 +1044,18 @@
}
}
+void Service::Timeout() {
+ // All process state flags will be taken care of in Reap(), we really just want to kill the
+ // process here when it times out. Oneshot processes will transition to be disabled, and
+ // all other processes will transition to be restarting.
+ LOG(INFO) << "Service '" << name_ << "' expired its timeout of " << timeout_period_->count()
+ << " seconds and will now be killed";
+ if (pid_) {
+ KillProcessGroup(SIGKILL);
+ NotifyStateChange("stopping");
+ }
+}
+
void Service::Restart() {
if (flags_ & SVC_RUNNING) {
/* Stop, wait, then start the service. */
diff --git a/init/service.h b/init/service.h
index ea79a07..e8d5ead 100644
--- a/init/service.h
+++ b/init/service.h
@@ -21,7 +21,9 @@
#include <sys/resource.h>
#include <sys/types.h>
+#include <chrono>
#include <memory>
+#include <optional>
#include <set>
#include <string>
#include <vector>
@@ -81,6 +83,7 @@
void Reset();
void Stop();
void Terminate();
+ void Timeout();
void Restart();
void Reap(const siginfo_t& siginfo);
void DumpState() const;
@@ -117,6 +120,8 @@
bool process_cgroup_empty() const { return process_cgroup_empty_; }
unsigned long start_order() const { return start_order_; }
void set_sigstop(bool value) { sigstop_ = value; }
+ std::chrono::seconds restart_period() const { return restart_period_; }
+ std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
const std::vector<std::string>& args() const { return args_; }
private:
@@ -153,11 +158,13 @@
Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
Result<Success> ParseNamespace(const std::vector<std::string>& args);
Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
+ Result<Success> ParseRestartPeriod(const std::vector<std::string>& args);
Result<Success> ParseSeclabel(const std::vector<std::string>& args);
Result<Success> ParseSetenv(const std::vector<std::string>& args);
Result<Success> ParseShutdown(const std::vector<std::string>& args);
Result<Success> ParseSigstop(const std::vector<std::string>& args);
Result<Success> ParseSocket(const std::vector<std::string>& args);
+ Result<Success> ParseTimeoutPeriod(const std::vector<std::string>& args);
Result<Success> ParseFile(const std::vector<std::string>& args);
Result<Success> ParseUser(const std::vector<std::string>& args);
Result<Success> ParseWritepid(const std::vector<std::string>& args);
@@ -220,6 +227,9 @@
bool sigstop_ = false;
+ std::chrono::seconds restart_period_ = 5s;
+ std::optional<std::chrono::seconds> timeout_period_;
+
std::vector<std::string> args_;
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;