Merge "init: allow customizable restart and timeout periods for services"

This commit is contained in:
Tom Cherry 2018-10-05 16:15:23 +00:00 committed by Gerrit Code Review
commit 5f2a21d244
4 changed files with 77 additions and 9 deletions

View File

@ -262,6 +262,13 @@ runs the service.
> 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 @@ runs the service.
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)

View File

@ -187,23 +187,34 @@ void property_changed(const std::string& name, const std::string& value) {
}
}
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 @@ int main(int argc, char** argv) {
}
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;
}
}

View File

@ -627,6 +627,15 @@ Result<Success> Service::ParseProcessRlimit(const std::vector<std::string>& args
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 @@ Result<Success> Service::ParseShutdown(const std::vector<std::string>& args) {
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 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
{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::Terminate() {
}
}
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. */

View File

@ -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 @@ class Service {
void Reset();
void Stop();
void Terminate();
void Timeout();
void Restart();
void Reap(const siginfo_t& siginfo);
void DumpState() const;
@ -117,6 +120,8 @@ class Service {
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 @@ class Service {
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 @@ class Service {
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_;