Merge "init: allow customizable restart and timeout periods for services"
This commit is contained in:
commit
5f2a21d244
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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_;
|
||||
|
|
Loading…
Reference in New Issue