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));
}
}
+ 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;
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;
}
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;
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)) {
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);
}
}
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);
}
}
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);
void base_process_service::emergency_stop() noexcept
{
- if (! do_auto_restart() && start_explicit) {
- start_explicit = false;
- release(false);
- }
forced_stop();
stop_dependents();
}
// 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;
}