From b0a0a7c9e4322df83d95249cb8f36049b1d7e8ab Mon Sep 17 00:00:00 2001 From: Davin McCall Date: Tue, 6 Jun 2017 09:07:01 +0100 Subject: [PATCH] Add restart-limit-count service setting. Specifies the maximum number of automatic restarts allowed within the restart limit interval. Can be set to 0 to disable restart limiting. --- README | 5 +++++ src/load_service.cc | 33 ++++++++++++++++++++++++++++----- src/service.cc | 22 ++++++++++++---------- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/README b/README index 34b5969..2b2c883 100644 --- a/README +++ b/README @@ -171,6 +171,11 @@ restart-limit-interval = XXX.YYYY 10 seconds. Use this to prevent broken services from continuously restarting ad infinitum. +restart-limit-count = NNN + Specifies the maximum number of times that a service can automatically + restart over the interval specified by restart-limit-interval (default of + 10 seconds). Specify a value of 0 to disable the restart limit. + pid-file = (path to file) For "bgprocess" type services only; specifies the path of the file where daemon will write its process ID before detaching. diff --git a/src/load_service.cc b/src/load_service.cc index e246958..7e2dd8b 100644 --- a/src/load_service.cc +++ b/src/load_service.cc @@ -208,12 +208,9 @@ static uid_t parse_uid_param(const std::string ¶m, const std::string &servic std::size_t ind = 0; try { // POSIX does not specify whether uid_t is an signed or unsigned, but regardless - // is is probably safe to assume that valid values are positive. We'll also assume - // that the value range fits with "unsigned long long" since it seems unlikely + // is is probably safe to assume that valid values are positive. We'll also assert + // that the value range fits within "unsigned long long" since it seems unlikely // that would ever not be the case. - // - // TODO perhaps write a number parser, since even the unsigned variants of the C/C++ - // functions accept a leading minus sign... static_assert((uintmax_t)std::numeric_limits::max() <= (uintmax_t)std::numeric_limits::max(), "uid_t is too large"); unsigned long long v = std::stoull(param, &ind, 0); if (v > static_cast(std::numeric_limits::max()) || ind != param.length()) { @@ -247,6 +244,28 @@ static uid_t parse_uid_param(const std::string ¶m, const std::string &servic return pwent->pw_uid; } +static const char * num_err_msg = "Specified value contains invalid numeric characters or is outside allowed range."; + +// Parse an unsigned numeric parameter value +static unsigned long long parse_unum_param(const std::string ¶m, const std::string &service_name, + unsigned long long max = std::numeric_limits::max()) +{ + std::size_t ind = 0; + try { + unsigned long long v = std::stoull(param, &ind, 0); + if (v > max || ind != param.length()) { + throw ServiceDescriptionExc(service_name, num_err_msg); + } + return v; + } + catch (std::out_of_range &exc) { + throw ServiceDescriptionExc(service_name, num_err_msg); + } + catch (std::invalid_argument &exc) { + throw ServiceDescriptionExc(service_name, num_err_msg); + } +} + static const char * gid_err_msg = "Specified group id contains invalid numeric characters or is outside allowed range."; static gid_t parse_gid_param(const std::string ¶m, const std::string &service_name) @@ -532,6 +551,10 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name) restart_interval.tv_sec = isec; restart_interval.tv_nsec = insec; } + else if (setting == "restart-limit-count") { + string limit_str = read_setting_value(i, end, nullptr); + max_restarts = parse_unum_param(limit_str, name, std::numeric_limits::max()); + } else { throw ServiceDescriptionExc(name, "Unknown setting: " + setting); } diff --git a/src/service.cc b/src/service.cc index 9104408..2160ed8 100644 --- a/src/service.cc +++ b/src/service.cc @@ -1245,17 +1245,19 @@ bool base_process_service::restart_ps_process() noexcept timespec current_time; eventLoop.get_time(current_time, clock_type::MONOTONIC); - // Check whether we're still in the most recent restart check interval: - timespec int_diff = diff_time(current_time, restart_interval_time); - if (int_diff < restart_interval) { - if (++restart_interval_count >= max_restart_interval_count) { - log(LogLevel::ERROR, "Service ", service_name, " restarting too quickly; stopping."); - return false; + if (max_restart_interval_count != 0) { + // Check whether we're still in the most recent restart check interval: + timespec int_diff = diff_time(current_time, restart_interval_time); + if (int_diff < restart_interval) { + if (++restart_interval_count >= max_restart_interval_count) { + log(LogLevel::ERROR, "Service ", service_name, " restarting too quickly; stopping."); + return false; + } + } + else { + restart_interval_time = current_time; + restart_interval_count = 0; } - } - else { - restart_interval_time = current_time; - restart_interval_count = 0; } // Check if enough time has lapsed since the prevous restart. If not, start a timer: -- 2.25.1