Add "smooth-recovery" option for process services.
authorDavin McCall <davmac@davmac.org>
Sat, 2 Jan 2016 13:24:31 +0000 (13:24 +0000)
committerDavin McCall <davmac@davmac.org>
Sat, 2 Jan 2016 13:24:31 +0000 (13:24 +0000)
A process service with smooth-recovery set will restart its
process (if it dies unexpectedly) without bring the service
itself down. I.e the service remains in STARTED state, and
any dependents do not have to be brought down.

README
load_service.cc
service.cc
service.h

diff --git a/README b/README
index da74f2515659a2196650fec576617b7183252d9d..883a2dcf3eb65c5c03011f6323c4971ad4d50472 100644 (file)
--- a/README
+++ b/README
@@ -82,12 +82,10 @@ Parameters are:
 
 type = process | scripted | internal
 command = ...
-restart = yes | true | no | false
 logfile = ...
 onstart = ...
 depends-on = (service name)
 waits-for = (service name)
-termsignal = HUP | INT | QUIT | USR1 | USR2
 
 command = (external script or executable, and arguments)
    For a 'process' service, this is the process to run.
@@ -95,7 +93,19 @@ command = (external script or executable, and arguments)
 
 stop-command = (external script or executable, and arguments)
    For a 'scripted' service, this command is run to stop the service.
+
+restart = yes | true | no | false
+   Specifies whether the service should automatically restart if it becomes
+   stopped (for any reason, including being explicitly requested to stop).
+
+smooth-recovery = yes | true | no | false
+   For process services only. Specifies that, should the process die, it
+   can be restarted without bringing the service itself down. This means that
+   any dependent services do not need to be stopped/restarted. Such recovery
+   happens regardless of the "restart" setting (if smooth-recovery is enabled,
+   the service does not reach the stopped state when the process terminates
+   unexpectedly).
+
 onstart = (internal commands)
    rw_ready - try again to open any logs, control socket etc that could not
               be opened previously due to a read-only filesystem.
@@ -112,7 +122,7 @@ waits-for = (service name)
    for this service. Starting this service will automatically start
    the named service.
 
-termsignal = (signal)
+termsignal = HUP | INT | QUIT | USR1 | USR2
    Specifies an additional signal to send to the process when requesting it
    to terminate (applies to 'process' services only). SIGTERM is always
    sent along with the specified signal, unless the 'nosigterm' setting is
index c99cedd27d1c03797e52644edd8b1cbf7826d6e5..845f5c243d4ec62631a89f5d7060d99027e87e2e 100644 (file)
@@ -217,9 +217,10 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name)
     string logfile;
     OnstartFlags onstart_flags;
     int term_signal = -1;  // additional termination signal
+    bool auto_restart = false;
+    bool smooth_recovery = false;
     
     string line;
-    bool auto_restart = false;
     ifstream service_file;
     service_file.exceptions(ios::badbit | ios::failbit);
     
@@ -276,6 +277,10 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name)
                     string restart = read_setting_value(i, end);
                     auto_restart = (restart == "yes" || restart == "true");
                 }
+                else if (setting == "smooth-recovery") {
+                    string recovery = read_setting_value(i, end);
+                    smooth_recovery = (recovery == "yes" || recovery == "true");
+                }
                 else if (setting == "type") {
                     string type_str = read_setting_value(i, end);
                     if (type_str == "scripted") {
@@ -342,6 +347,7 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name)
                 rval->setStopCommand(stop_command, stop_command_offsets);
                 rval->setLogfile(logfile);
                 rval->setAutoRestart(auto_restart);
+                rval->setSmoothRecovery(smooth_recovery);
                 rval->setOnstartFlags(onstart_flags);
                 rval->setExtraTerminationSignal(term_signal);
                 *iter = rval;
index d7dd13800b7a936c17b949db78eb38a1be5763fe..d893a6d280b12a8d25f2e431ce08f5c8db99d5a9 100644 (file)
@@ -108,6 +108,14 @@ void ServiceRecord::handle_exit_status() noexcept
         if (service_state == ServiceState::STOPPING) {
             stopped();
         }
+        else if (smooth_recovery) {
+            // TODO ensure a minimum time between restarts
+            // TODO if we are pinned-started then we should probably check
+            //      that dependencies have started before trying to re-start the
+            //      service process.
+            start_ps_process();
+            return;
+        }
         else {
             forceStop();
         }
@@ -168,7 +176,9 @@ void ServiceRecord::process_child_status(struct ev_loop *loop, ev_io * stat_io,
     else {
         // exec() succeeded.
         if (sr->service_type == ServiceType::PROCESS) {
-            sr->started();
+            if (sr->service_state != ServiceState::STARTED) {
+                sr->started();
+            }
             if (sr->pid == -1) {
                 // Somehow the process managed to complete before we even saw the status.
                 sr->handle_exit_status();
index abed4511890aac75afecfcb3e0429548dd3340fa..b890c32dd5bbd85ac54655918de59354950ef655 100644 (file)
--- a/service.h
+++ b/service.h
@@ -163,8 +163,9 @@ class ServiceRecord
     
     OnstartFlags onstart_flags;
 
-    string logfile; /* log file name, empty string specifies /dev/null */
-    bool auto_restart : 1; /* whether to restart this (process) if it dies unexpectedly */
+    string logfile;           // log file name, empty string specifies /dev/null
+    bool auto_restart : 1;    // whether to restart this (process) if it dies unexpectedly
+    bool smooth_recovery : 1; // whether the service process can restart without bringing down service
     bool pinned_stopped : 1;
     bool pinned_started : 1;
     
@@ -358,6 +359,11 @@ class ServiceRecord
         this->auto_restart = auto_restart;
     }
     
+    void setSmoothRecovery(bool smooth_recovery) noexcept
+    {
+        this->smooth_recovery = smooth_recovery;
+    }
+    
     // Set "on start" flags (commands)
     void setOnstartFlags(OnstartFlags flags) noexcept
     {