Fix missing #include (needed for compiling with GCC 10)
[oweals/dinit.git] / src / baseproc-service.cc
index e9cfa2cf4afc6d7d2394a1992ca873984a74ec80..32c49d5969600901f3ae2f59815ea62f1717cc49 100644 (file)
@@ -86,6 +86,11 @@ bool base_process_service::start_ps_process(const std::vector<const char *> &cmd
     control_conn_t *control_conn = nullptr;
 
     int control_socket[2] = {-1, -1};
+    int notify_pipe[2] = {-1, -1};
+    bool have_notify = !notification_var.empty() || force_notification_fd != -1;
+    ready_notify_watcher * rwatcher = have_notify ? get_ready_watcher() : nullptr;
+    bool ready_watcher_registered = false;
+
     if (onstart_flags.pass_cs_fd) {
         if (dinit_socketpair(AF_UNIX, SOCK_STREAM, /* protocol */ 0, control_socket, SOCK_NONBLOCK)) {
             log(loglevel_t::ERROR, get_name(), ": can't create control socket: ", strerror(errno));
@@ -105,6 +110,27 @@ bool base_process_service::start_ps_process(const std::vector<const char *> &cmd
         }
     }
 
+    if (have_notify) {
+        // Create a notification pipe:
+        if (bp_sys::pipe2(notify_pipe, 0) != 0) {
+            log(loglevel_t::ERROR, get_name(), ": can't create notification pipe: ", strerror(errno));
+            goto out_cs_h;
+        }
+
+        // Set the read side as close-on-exec:
+        int fdflags = bp_sys::fcntl(notify_pipe[0], F_GETFD);
+        bp_sys::fcntl(notify_pipe[0], F_SETFD, fdflags | FD_CLOEXEC);
+
+        // add, but don't yet enable, readiness watcher:
+        try {
+            rwatcher->add_watch(event_loop, notify_pipe[0], dasynq::IN_EVENTS, false);
+            ready_watcher_registered = true;
+        }
+        catch (std::exception &exc) {
+            log(loglevel_t::ERROR, get_name(), ": can't add notification watch: ", exc.what());
+        }
+    }
+
     // Set up complete, now fork and exec:
 
     pid_t forkpid;
@@ -128,17 +154,26 @@ bool base_process_service::start_ps_process(const std::vector<const char *> &cmd
     if (forkpid == 0) {
         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);
+        after_fork(getpid());
+        run_proc_params run_params{cmd.data(), working_dir_c, logfile, pipefd[1], run_as_uid, run_as_gid, rlimits};
+        run_params.on_console = on_console;
+        run_params.in_foreground = !onstart_flags.shares_console;
+        run_params.csfd = control_socket[1];
+        run_params.socket_fd = socket_fd;
+        run_params.notify_fd = notify_pipe[1];
+        run_params.force_notify_fd = force_notification_fd;
+        run_params.notify_var = notification_var.c_str();
+        run_params.env_file = env_file.c_str();
+        run_child_proc(run_params);
     }
     else {
         // Parent process
-        bp_sys::close(pipefd[1]); // close the 'other end' fd
-        if (control_socket[1] != -1) {
-            bp_sys::close(control_socket[1]);
-        }
         pid = forkpid;
 
+        bp_sys::close(pipefd[1]); // close the 'other end' fd
+        if (control_socket[1] != -1) bp_sys::close(control_socket[1]);
+        if (notify_pipe[1] != -1) bp_sys::close(notify_pipe[1]);
+        notification_fd = notify_pipe[0];
         waiting_for_execstat = true;
         return true;
     }
@@ -150,6 +185,12 @@ bool base_process_service::start_ps_process(const std::vector<const char *> &cmd
         child_status_listener.deregister(event_loop);
     }
 
+    if (notify_pipe[0] != -1) bp_sys::close(notify_pipe[0]);
+    if (notify_pipe[1] != -1) bp_sys::close(notify_pipe[1]);
+    if (ready_watcher_registered) {
+        rwatcher->deregister(event_loop);
+    }
+
     if (onstart_flags.pass_cs_fd) {
         delete control_conn;
 
@@ -263,7 +304,8 @@ bool base_process_service::interrupt_start() noexcept
         return service_record::interrupt_start();
     }
     else {
-        log(loglevel_t::WARN, "Interrupting start of service ", get_name(), " with pid ", pid, " (with SIGINT).");
+        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)) {
@@ -283,7 +325,8 @@ bool base_process_service::interrupt_start() noexcept
 void base_process_service::kill_with_fire() noexcept
 {
     if (pid != -1) {
-        log(loglevel_t::WARN, "Service ", get_name(), " with pid ", pid, " exceeded allowed stop time; killing.");
+        log(loglevel_t::WARN, "Service ", get_name(), " with pid ", pid,
+                " exceeded allowed stop time; killing.");
         kill_pg(SIGKILL);
     }
 }
@@ -296,9 +339,10 @@ void base_process_service::kill_pg(int signo) noexcept
     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;
+            // On some OSes (eg OpenBSD) we aren't allowed to get the pgid of a process in a different
+            // session. If the process is in a different session, however, it must be a process group
+            // leader and the pgid must equal the process id.
+            pgid = pid;
         }
         bp_sys::kill(-pgid, signo);
     }
@@ -317,7 +361,8 @@ void base_process_service::timer_expired() noexcept
     }
     else if (pid != -1) {
         // Starting, start timed out.
-        log(loglevel_t::WARN, "Service ", get_name(), " with pid ", pid, " exceeded allowed start time; cancelling.");
+        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);
@@ -330,10 +375,6 @@ void base_process_service::timer_expired() noexcept
 
 void base_process_service::emergency_stop() noexcept
 {
-    if (! do_auto_restart() && start_explicit) {
-        start_explicit = false;
-        release(false);
-    }
     forced_stop();
     stop_dependents();
 }
@@ -404,13 +445,15 @@ bool base_process_service::open_socket() noexcept
     // POSIX (1003.1, 2013) says that fchown and fchmod don't necessarily work on sockets. We have to
     // use chown and chmod instead.
     if (chown(saddrname, socket_uid, socket_gid)) {
-        log(loglevel_t::ERROR, get_name(), ": Error setting activation socket owner/group: ", strerror(errno));
+        log(loglevel_t::ERROR, get_name(), ": Error setting activation socket owner/group: ",
+                strerror(errno));
         close(sockfd);
         return false;
     }
 
     if (chmod(saddrname, socket_perms) == -1) {
-        log(loglevel_t::ERROR, get_name(), ": Error setting activation socket permissions: ", strerror(errno));
+        log(loglevel_t::ERROR, get_name(), ": Error setting activation socket permissions: ",
+                strerror(errno));
         close(sockfd);
         return false;
     }