Implement a "shares-console" option for non-exclusive console access.
[oweals/dinit.git] / src / baseproc-service.cc
index e53028cf09e8a8b709545e0b32d7c87152d79cee..1a2be988b22ccecbda423dc3cc290e5c4cca5134 100644 (file)
@@ -1,4 +1,5 @@
 #include <cstring>
+#include <cstdlib>
 
 #include <sys/un.h>
 #include <sys/socket.h>
@@ -42,7 +43,8 @@ bool base_process_service::bring_up() noexcept
 
         event_loop.get_time(restart_interval_time, clock_type::MONOTONIC);
         restart_interval_count = 0;
-        if (start_ps_process(exec_arg_parts, onstart_flags.starts_on_console)) {
+        if (start_ps_process(exec_arg_parts,
+                onstart_flags.starts_on_console || onstart_flags.shares_console)) {
             // Note: we don't set a start timeout for PROCESS services.
             if (start_timeout != time_val(0,0) && get_type() != service_type_t::PROCESS) {
                 restart_timer.arm_timer_rel(event_loop, start_timeout);
@@ -122,8 +124,10 @@ bool base_process_service::start_ps_process(const std::vector<const char *> &cmd
     }
 
     if (forkpid == 0) {
-        run_child_proc(cmd.data(), logfile, on_console, pipefd[1], control_socket[1], socket_fd,
-                run_as_uid, run_as_gid);
+        const char * working_dir_c = nullptr;
+        if (! working_dir.empty()) working_dir_c = working_dir.c_str();
+        run_child_proc(cmd.data(), working_dir_c, logfile, on_console, pipefd[1], control_socket[1],
+                socket_fd, run_as_uid, run_as_gid);
     }
     else {
         // Parent process
@@ -161,7 +165,7 @@ bool base_process_service::start_ps_process(const std::vector<const char *> &cmd
 
 base_process_service::base_process_service(service_set *sset, string name,
         service_type_t service_type_p, string &&command,
-        std::list<std::pair<unsigned,unsigned>> &command_offsets,
+        const std::list<std::pair<unsigned,unsigned>> &command_offsets,
         const std::list<prelim_dep> &deplist_p)
      : service_record(sset, name, service_type_p, deplist_p), child_listener(this),
        child_status_listener(this), restart_timer(this)
@@ -183,7 +187,6 @@ base_process_service::base_process_service(service_set *sset, string name,
     reserved_child_watch = false;
     tracking_child = false;
     stop_timer_armed = false;
-    start_is_interruptible = false;
 }
 
 void base_process_service::do_restart() noexcept
@@ -201,7 +204,7 @@ void base_process_service::do_restart() noexcept
         }
     }
 
-    if (! start_ps_process(exec_arg_parts, have_console)) {
+    if (! start_ps_process(exec_arg_parts, have_console || onstart_flags.shares_console)) {
         restarting = false;
         if (service_state == service_state_t::STARTING) {
             failed_to_start();
@@ -260,6 +263,7 @@ bool base_process_service::interrupt_start() noexcept
     else {
         log(loglevel_t::WARN, "Interrupting start of service ", get_name(), " with pid ", pid, " (with SIGINT).");
         kill_pg(SIGINT);
+
         if (stop_timeout != time_val(0,0)) {
             restart_timer.arm_timer_rel(event_loop, stop_timeout);
             stop_timer_armed = true;
@@ -268,8 +272,8 @@ bool base_process_service::interrupt_start() noexcept
             restart_timer.stop_timer(event_loop);
             stop_timer_armed = false;
         }
+
         set_state(service_state_t::STOPPING);
-        notify_listeners(service_event_t::STARTCANCELLED);
         return false;
     }
 }
@@ -284,13 +288,18 @@ void base_process_service::kill_with_fire() noexcept
 
 void base_process_service::kill_pg(int signo) noexcept
 {
-    pid_t pgid = bp_sys::getpgid(pid);
-    if (pgid == -1) {
-        // only should happen if pid is invalid, which should never happen...
-        log(loglevel_t::ERROR, get_name(), ": can't signal process: ", strerror(errno));
-        return;
+    if (onstart_flags.signal_process_only) {
+        bp_sys::kill(pid, signo);
+    }
+    else {
+        pid_t pgid = bp_sys::getpgid(pid);
+        if (pgid == -1) {
+            // only should happen if pid is invalid, which should never happen...
+            log(loglevel_t::ERROR, get_name(), ": can't signal process: ", strerror(errno));
+            return;
+        }
+        bp_sys::kill(-pgid, signo);
     }
-    bp_sys::kill(-pgid, signo);
 }
 
 void base_process_service::timer_expired() noexcept
@@ -306,12 +315,10 @@ void base_process_service::timer_expired() noexcept
     }
     else if (pid != -1) {
         // Starting, start timed out.
-        stop_dependents();
-        if (start_explicit) {
-            start_explicit = false;
-            release();
-        }
+        log(loglevel_t::WARN, "Service ", get_name(), " with pid ", pid, " exceeded allowed start time; cancelling.");
         interrupt_start();
+        stop_reason = stopped_reason_t::TIMEDOUT;
+        failed_to_start(false, false);
     }
     else {
         // STARTING / STARTED, and we have a pid: must be restarting (smooth recovery if STARTED)
@@ -327,7 +334,6 @@ void base_process_service::emergency_stop() noexcept
     }
     forced_stop();
     stop_dependents();
-    stopped();
 }
 
 void base_process_service::becoming_inactive() noexcept