Fix restart after unexpected termination.
[oweals/dinit.git] / src / service.cc
index 7c87cd0a25063ec5ea9b05c27779aabf047b29af..504a9b6de0836710449785effbac4a3f04b97452 100644 (file)
@@ -26,7 +26,7 @@ static service_record * find_service(const std::list<service_record *> & records
 {
     using std::list;
     list<service_record *>::const_iterator i = records.begin();
-    for ( ; i != records.end(); i++ ) {
+    for ( ; i != records.end(); ++i ) {
         if (strcmp((*i)->get_name().c_str(), name) == 0) {
             return *i;
         }
@@ -54,16 +54,16 @@ void service_record::stopped() noexcept
 {
     if (have_console) {
         bp_sys::tcsetpgrp(0, bp_sys::getpgrp());
-        discard_console_log_buffer();
         release_console();
     }
 
     force_stop = false;
 
-    // If we are a soft dependency of another target, break the acquisition from that target now:
+    // If we are a soft dependency of another target, break the acquisition from that target now,
+    // so that we don't re-start:
     for (auto & dependent : dependents) {
         if (dependent->dep_type != dependency_type::REGULAR) {
-            if (dependent->holding_acq) {
+            if (dependent->holding_acq  && ! dependent->waiting_on) {
                 dependent->holding_acq = false;
                 release();
             }
@@ -71,9 +71,9 @@ void service_record::stopped() noexcept
     }
 
     bool will_restart = (desired_state == service_state_t::STARTED)
-            && services->get_auto_restart();
+            && !services->is_shutting_down();
 
-    for (auto dependency : depends_on) {
+    for (auto dependency : depends_on) {
         // we signal dependencies in case they are waiting for us to stop:
         dependency.get_to()->dependent_stopped();
     }
@@ -104,14 +104,33 @@ void service_record::stopped() noexcept
         }
     }
 
-    log_service_stopped(service_name);
+    // Start failure will have been logged already, only log if we are stopped for other reasons:
+    if (! start_failed) {
+        log_service_stopped(service_name);
+
+        // If this service chains to another, start the other service now:
+        if (! will_restart && ! start_on_completion.empty()) {
+            try {
+                auto chain_to = services->load_service(start_on_completion.c_str());
+                chain_to->start();
+            }
+            catch (service_load_exc &sle) {
+                log(loglevel_t::ERROR, "Couldn't chain to service ", start_on_completion, ": ",
+                        "couldn't load ", sle.service_name, ": ", sle.exc_description);
+            }
+            catch (std::bad_alloc &bae) {
+                log(loglevel_t::ERROR, "Couldn't chain to service ", start_on_completion,
+                        ": Out of memory");
+            }
+        }
+    }
     notify_listeners(service_event_t::STOPPED);
 }
 
 bool service_record::do_auto_restart() noexcept
 {
     if (auto_restart) {
-        return services->get_auto_restart();
+        return !services->is_shutting_down();
     }
     return false;
 }
@@ -143,6 +162,7 @@ void service_record::release(bool issue_stop) noexcept
             services->service_inactive(this);
         }
         else if (issue_stop) {
+               stop_reason = stopped_reason_t::NORMAL;
             do_stop();
         }
     }
@@ -188,6 +208,8 @@ void service_record::start(bool activate) noexcept
         services->service_active(this);
     }
 
+    start_failed = false;
+    start_skipped = false;
     service_state = service_state_t::STARTING;
     waiting_for_deps = true;
 
@@ -214,6 +236,7 @@ void service_record::do_propagation() noexcept
     
     if (prop_failure) {
         prop_failure = false;
+        stop_reason = stopped_reason_t::DEPFAILED;
         failed_to_start(true);
     }
     
@@ -355,7 +378,7 @@ void service_record::started() noexcept
     notify_listeners(service_event_t::STARTED);
 
     if (onstart_flags.rw_ready) {
-        open_control_socket();
+        rootfs_is_rw();
     }
     if (onstart_flags.log_ready) {
         setup_external_log();
@@ -412,6 +435,7 @@ void service_record::failed_to_start(bool depfailed, bool immediate_stop) noexce
         }
     }
 
+    start_failed = true;
     log_service_failed(get_name());
     notify_listeners(service_event_t::FAILEDSTART);
 
@@ -434,7 +458,7 @@ void service_record::forced_stop() noexcept
         force_stop = true;
         if (! pinned_started) {
             prop_stop = true;
-            services->add_transition_queue(this);
+            services->add_prop_queue(this);
         }
     }
 }
@@ -453,7 +477,9 @@ void service_record::stop(bool bring_down) noexcept
         release();
     }
 
-    if (bring_down) {
+    if (bring_down && service_state != service_state_t::STOPPED
+               && service_state != service_state_t::STOPPING) {
+       stop_reason = stopped_reason_t::NORMAL;
         do_stop();
     }
 }
@@ -475,8 +501,8 @@ void service_record::do_stop() noexcept
             // we need to delegate to can_interrupt_start() (which can be overridden).
             if (! waiting_for_deps && ! waiting_for_console) {
                 if (! can_interrupt_start()) {
-                    // Well this is awkward: we're going to have to continue starting. We can stop once we've
-                    // reached the started state.
+                    // Well this is awkward: we're going to have to continue starting. We can stop once
+                    // we've reached the started state.
                     return;
                 }
 
@@ -557,7 +583,9 @@ bool service_record::stop_dependents() noexcept
             }
             if (dept->holding_acq) {
                 dept->holding_acq = false;
-                release();
+                // release without issuing stop, since we should be called only when this
+                // service is already stopped/stopping:
+                release(false);
             }
         }
     }