Add restart-limit-count service setting.
authorDavin McCall <davmac@davmac.org>
Tue, 6 Jun 2017 08:07:01 +0000 (09:07 +0100)
committerDavin McCall <davmac@davmac.org>
Tue, 6 Jun 2017 08:07:01 +0000 (09:07 +0100)
Specifies the maximum number of automatic restarts allowed within the
restart limit interval. Can be set to 0 to disable restart limiting.

README
src/load_service.cc
src/service.cc

diff --git a/README b/README
index 34b5969e14e5ad6503cb9191e0238e986c859d83..2b2c88391465c3c187face9ae3cf7c0ad1cb40d1 100644 (file)
--- 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.
index e2469586be4df71cf3219a35a88f5c47675155ce..7e2dd8b19401a2fd62c76516660dc3ad508b8098 100644 (file)
@@ -208,12 +208,9 @@ static uid_t parse_uid_param(const std::string &param, 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<uid_t>::max() <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "uid_t is too large");
         unsigned long long v = std::stoull(param, &ind, 0);
         if (v > static_cast<unsigned long long>(std::numeric_limits<uid_t>::max()) || ind != param.length()) {
@@ -247,6 +244,28 @@ static uid_t parse_uid_param(const std::string &param, 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 &param, const std::string &service_name,
+        unsigned long long max = std::numeric_limits<unsigned long long>::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 &param, 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<int>::max());
+                }
                 else {
                     throw ServiceDescriptionExc(name, "Unknown setting: " + setting);
                 }
index 9104408c87e8d6a79af4daff8611379e0f1b7ad0..2160ed8640879b5b3b0bf78140c92b2b00b35bb5 100644 (file)
@@ -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: