Log failure stage as well as error-from-errno.
{ }
};
+enum class exec_stage {
+ ARRANGE_FDS, READ_ENV_FILE, SET_NOTIFYFD_VAR, SETUP_ACTIVATION_SOCKET, SETUP_CONTROL_SOCKET,
+ CHDIR, SETUP_STDINOUTERR, SET_RLIMITS, SET_UIDGID, /* must be last: */ DO_EXEC
+};
+
+extern const char * const exec_stage_descriptions[static_cast<int>(exec_stage::DO_EXEC) + 1];
+
+// Error information from process execution transferred via this struct
+struct run_proc_err
+{
+ exec_stage stage;
+ int st_errno;
+};
+
class base_process_service;
// A timer for process restarting. Used to ensure a minimum delay between process restarts (and
virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept = 0;
// Called if an exec fails.
- virtual void exec_failed(int errcode) noexcept = 0;
+ virtual void exec_failed(run_proc_err errcode) noexcept = 0;
// Called if exec succeeds.
virtual void exec_succeeded() noexcept { };
class process_service : public base_process_service
{
virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept override;
- virtual void exec_failed(int errcode) noexcept override;
+ virtual void exec_failed(run_proc_err errcode) noexcept override;
virtual void exec_succeeded() noexcept override;
virtual void bring_down() noexcept override;
class bgproc_service : public base_process_service
{
virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept override;
- virtual void exec_failed(int errcode) noexcept override;
+ virtual void exec_failed(run_proc_err errcode) noexcept override;
virtual void bring_down() noexcept override;
enum class pid_result_t {
{
virtual void handle_exit_status(bp_sys::exit_status exit_status) noexcept override;
virtual void exec_succeeded() noexcept override;
- virtual void exec_failed(int errcode) noexcept override;
+ virtual void exec_failed(run_proc_err errcode) noexcept override;
virtual void bring_down() noexcept override;
virtual bool interrupt_start() noexcept override
* See proc-service.h header for interface details.
*/
+// Strings describing the execution stages (failure points).
+const char * const exec_stage_descriptions[static_cast<int>(exec_stage::DO_EXEC) + 1] = {
+ "arranging file descriptors", // ARRANGE_FDS
+ "reading environment file", // READ_ENV_FILE
+ "setting environment variable", // SET_NOTIFYFD_VAR
+ "setting up activation socket", // SETUP_ACTIVATION_SOCKET
+ "setting up control socket", // SETUP_CONTROL_SOCKET
+ "changing directory", // CHDIR
+ "setting up standard input/output descriptors", // SETUP_STDINOUTERR
+ "setting resource limits", // SET_RLIMITS
+ "setting user/group ID", // SET_UIDGID
+ "executing command" // DO_EXEC
+};
+
// Given a string and a list of pairs of (start,end) indices for each argument in that string,
// store a null terminator for the argument. Return a `char *` vector containing the beginning
// of each argument and a trailing nullptr. (The returned array is invalidated if the string is
base_process_service *sr = service;
sr->waiting_for_execstat = false;
- int exec_status;
- int r = read(get_watched_fd(), &exec_status, sizeof(int));
+ run_proc_err exec_status;
+ int r = read(get_watched_fd(), &exec_status, sizeof(exec_status));
deregister(loop);
close(get_watched_fd());
services->process_queues();
}
-void process_service::exec_failed(int errcode) noexcept
+void process_service::exec_failed(run_proc_err errcode) noexcept
{
- log(loglevel_t::ERROR, get_name(), ": execution failed: ", strerror(errcode));
+ log(loglevel_t::ERROR, get_name(), ": execution failed - ",
+ exec_stage_descriptions[static_cast<int>(errcode.stage)], strerror(errcode.st_errno));
if (notification_fd != -1) {
readiness_watcher.deregister(event_loop);
services->process_queues();
}
-void bgproc_service::exec_failed(int errcode) noexcept
+void bgproc_service::exec_failed(run_proc_err errcode) noexcept
{
- log(loglevel_t::ERROR, get_name(), ": execution failed: ", strerror(errcode));
+ log(loglevel_t::ERROR, get_name(), ": execution failed - ",
+ exec_stage_descriptions[static_cast<int>(errcode.stage)], strerror(errcode.st_errno));
+
// Only time we execute is for startup:
stop_reason = stopped_reason_t::EXECFAILED;
failed_to_start();
}
}
-void scripted_service::exec_failed(int errcode) noexcept
+void scripted_service::exec_failed(run_proc_err errcode) noexcept
{
- log(loglevel_t::ERROR, get_name(), ": execution failed: ", strerror(errcode));
+ log(loglevel_t::ERROR, get_name(), ": execution failed - ",
+ exec_stage_descriptions[static_cast<int>(errcode.stage)], strerror(errcode.st_errno));
auto service_state = get_state();
if (service_state == service_state_t::STARTING) {
stop_reason = stopped_reason_t::EXECFAILED;
constexpr int csenvbufsz = 12 + ((CHAR_BIT * sizeof(int) - 1 + 2) / 3) + 1;
char csenvbuf[csenvbufsz];
+ run_proc_err err;
+ err.stage = exec_stage::ARRANGE_FDS;
+
int minfd = (socket_fd == -1) ? 3 : 4;
if (force_notify_fd != -1) {
if (notify_fd == -1) goto failure_out;
}
+ err.stage = exec_stage::READ_ENV_FILE;
+
// Read environment from file
if (params.env_file != nullptr) {
try {
// Set up notify-fd variable:
if (notify_var != nullptr && *notify_var != 0) {
+ err.stage = exec_stage::SET_NOTIFYFD_VAR;
// We need to do an allocation: the variable name length, '=', and space for the value,
// and nul terminator:
int notify_var_len = strlen(notify_var);
// Set up Systemd-style socket activation:
if (socket_fd != -1) {
+ err.stage = exec_stage::SETUP_ACTIVATION_SOCKET;
+
// If we passing a pre-opened socket, it has to be fd number 3. (Thanks, Systemd).
if (dup2(socket_fd, 3) == -1) goto failure_out;
if (socket_fd != 3) close(socket_fd);
}
if (csfd != -1) {
+ err.stage = exec_stage::SETUP_CONTROL_SOCKET;
snprintf(csenvbuf, csenvbufsz, "DINIT_CS_FD=%d", csfd);
if (putenv(csenvbuf)) goto failure_out;
}
if (working_dir != nullptr && *working_dir != 0) {
+ err.stage = exec_stage::CHDIR;
if (chdir(working_dir) == -1) {
goto failure_out;
}
if (i != force_notify_fd) close(i);
}
+ err.stage = exec_stage::SETUP_STDINOUTERR;
if (notify_fd == 0 || move_fd(open("/dev/null", O_RDONLY), 0) == 0) {
// stdin = 0. That's what we should have; proceed with opening stdout and stderr. We have to
// take care not to clobber the notify_fd.
}
// Resource limits
+ err.stage = exec_stage::SET_RLIMITS;
for (auto &limit : rlimits) {
rlimit setlimits;
if (!limit.hard_set || !limit.soft_set) {
}
if (uid != uid_t(-1)) {
+ err.stage = exec_stage::SET_UIDGID;
if (setreuid(uid, uid) != 0) goto failure_out;
if (setregid(gid, gid) != 0) goto failure_out;
}
sigprocmask(SIG_SETMASK, &sigwait_set, nullptr);
+ err.stage = exec_stage::DO_EXEC;
execvp(args[0], const_cast<char **>(args));
// If we got here, the exec failed:
failure_out:
- int exec_status = errno;
- write(wpipefd, &exec_status, sizeof(int));
+ err.st_errno = errno;
+ write(wpipefd, &err, sizeof(err));
_exit(0);
}
static void exec_failed(base_process_service *bsp, int errcode)
{
+ run_proc_err err;
+ err.stage = exec_stage::DO_EXEC;
+ err.st_errno = errcode;
bsp->waiting_for_execstat = false;
- bsp->exec_failed(errcode);
+ bsp->exec_failed(err);
}
static void handle_exit(base_process_service *bsp, int exit_status)