2016-05-03 12:28:06 +08:00
|
|
|
// Functions that we may safely call after fork().
|
2016-05-19 06:30:21 +08:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
|
|
|
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <errno.h>
|
2016-05-03 12:28:06 +08:00
|
|
|
#include <fcntl.h>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
2019-05-05 18:09:25 +08:00
|
|
|
#include <cstring>
|
2016-04-21 14:00:54 +08:00
|
|
|
#include <memory>
|
|
|
|
#if FISH_USE_POSIX_SPAWN
|
|
|
|
#include <spawn.h>
|
|
|
|
#endif
|
2019-03-13 05:06:01 +08:00
|
|
|
#include <cwchar>
|
2016-04-21 14:00:54 +08:00
|
|
|
|
2015-07-25 23:14:25 +08:00
|
|
|
#include "common.h"
|
2012-02-29 07:11:46 +08:00
|
|
|
#include "exec.h"
|
2019-05-19 05:51:16 +08:00
|
|
|
#include "flog.h"
|
2016-04-21 14:00:54 +08:00
|
|
|
#include "io.h"
|
2016-05-03 12:28:06 +08:00
|
|
|
#include "iothread.h"
|
|
|
|
#include "postfork.h"
|
|
|
|
#include "proc.h"
|
2019-01-29 06:35:56 +08:00
|
|
|
#include "redirection.h"
|
2016-05-03 12:28:06 +08:00
|
|
|
#include "signal.h"
|
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2012-02-29 07:11:46 +08:00
|
|
|
|
2013-01-17 19:46:33 +08:00
|
|
|
#ifndef JOIN_THREADS_BEFORE_FORK
|
2013-02-01 07:57:08 +08:00
|
|
|
#define JOIN_THREADS_BEFORE_FORK 0
|
2013-01-17 19:46:33 +08:00
|
|
|
#endif
|
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
/// The number of times to try to call fork() before giving up.
|
2012-02-29 07:11:46 +08:00
|
|
|
#define FORK_LAPS 5
|
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
/// The number of nanoseconds to sleep between attempts to call fork().
|
2012-02-29 07:11:46 +08:00
|
|
|
#define FORK_SLEEP_TIME 1000000
|
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
/// Fork error message.
|
2012-03-09 15:21:07 +08:00
|
|
|
#define FORK_ERROR "Could not create child process - exiting"
|
2012-02-29 07:11:46 +08:00
|
|
|
|
2017-07-27 10:45:22 +08:00
|
|
|
/// Called only by the child to set its own process group (possibly creating a new group in the
|
2018-01-21 16:20:01 +08:00
|
|
|
/// process if it is the first in a JOB_CONTROL job.
|
2016-12-15 11:21:36 +08:00
|
|
|
/// Returns true on sucess, false on failiure.
|
2017-07-27 10:45:22 +08:00
|
|
|
bool child_set_group(job_t *j, process_t *p) {
|
2019-06-24 03:39:29 +08:00
|
|
|
if (j->wants_job_control()) {
|
2018-10-02 11:55:18 +08:00
|
|
|
if (j->pgid == INVALID_PID) {
|
2012-11-19 08:30:30 +08:00
|
|
|
j->pgid = p->pid;
|
|
|
|
}
|
2018-10-10 03:37:11 +08:00
|
|
|
|
|
|
|
for (int i = 0; setpgid(p->pid, j->pgid) != 0; ++i) {
|
|
|
|
// Put a cap on how many times we retry so we are never stuck here
|
|
|
|
if (i < 100) {
|
|
|
|
if (errno == EPERM) {
|
2019-05-05 18:09:25 +08:00
|
|
|
// The setpgid(2) man page says that EPERM is returned only if attempts are made
|
|
|
|
// to move processes into groups across session boundaries (which can never be
|
|
|
|
// the case in fish, anywhere) or to change the process group ID of a session
|
|
|
|
// leader (again, can never be the case). I'm pretty sure this is a WSL bug, as
|
|
|
|
// we see the same with tcsetpgrp(2) in other places and it disappears on retry.
|
2018-10-10 03:37:11 +08:00
|
|
|
debug_safe(2, "setpgid(2) returned EPERM. Retrying");
|
|
|
|
continue;
|
|
|
|
} else if (errno == EINTR) {
|
2019-05-05 18:09:25 +08:00
|
|
|
// I don't think signals are blocked here. The parent (fish) redirected the
|
2019-06-23 06:10:54 +08:00
|
|
|
// signal handlers and `child_setup_process()` calls `signal_reset_handlers()`
|
2019-05-05 18:09:25 +08:00
|
|
|
// after we're done here (and not `signal_unblock()`). We're already in a loop,
|
|
|
|
// so let's just handle EINTR just in case.
|
2018-10-10 03:37:11 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-27 10:45:22 +08:00
|
|
|
char pid_buff[128];
|
|
|
|
char job_id_buff[128];
|
|
|
|
char getpgid_buff[128];
|
|
|
|
char job_pgid_buff[128];
|
|
|
|
char argv0[64];
|
|
|
|
char command[64];
|
|
|
|
|
|
|
|
format_long_safe(pid_buff, p->pid);
|
|
|
|
format_long_safe(job_id_buff, j->job_id);
|
|
|
|
format_long_safe(getpgid_buff, getpgid(p->pid));
|
|
|
|
format_long_safe(job_pgid_buff, j->pgid);
|
|
|
|
narrow_string_safe(argv0, p->argv0());
|
|
|
|
narrow_string_safe(command, j->command_wcstr());
|
|
|
|
|
|
|
|
debug_safe(
|
|
|
|
1, "Could not send own process %s, '%s' in job %s, '%s' from group %s to group %s",
|
|
|
|
pid_buff, argv0, job_id_buff, command, getpgid_buff, job_pgid_buff);
|
|
|
|
|
2018-10-31 14:23:57 +08:00
|
|
|
if (is_windows_subsystem_for_linux() && errno == EPERM) {
|
2019-05-05 18:09:25 +08:00
|
|
|
debug_safe(
|
|
|
|
1,
|
|
|
|
"Please update to Windows 10 1809/17763 or higher to address known issues "
|
|
|
|
"with process groups and zombie processes.");
|
2018-10-31 14:23:57 +08:00
|
|
|
}
|
|
|
|
|
2017-07-27 10:45:22 +08:00
|
|
|
safe_perror("setpgid");
|
2018-10-10 03:37:11 +08:00
|
|
|
|
|
|
|
return false;
|
2017-07-27 10:45:22 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
j->pgid = getpgrp();
|
|
|
|
}
|
2018-10-10 03:37:11 +08:00
|
|
|
return true;
|
2017-07-27 10:45:22 +08:00
|
|
|
}
|
|
|
|
|
2017-08-07 07:05:51 +08:00
|
|
|
/// Called only by the parent only after a child forks and successfully calls child_set_group,
|
|
|
|
/// guaranteeing the job control process group has been created and that the child belongs to the
|
|
|
|
/// correct process group. Here we can update our job_t structure to reflect the correct process
|
|
|
|
/// group in the case of JOB_CONTROL, and we can give the new process group control of the terminal
|
2018-02-03 06:31:10 +08:00
|
|
|
/// if it's to run in the foreground.
|
2017-07-27 10:45:22 +08:00
|
|
|
bool set_child_group(job_t *j, pid_t child_pid) {
|
2019-06-24 03:39:29 +08:00
|
|
|
if (j->wants_job_control()) {
|
2019-05-05 18:09:25 +08:00
|
|
|
assert(j->pgid != INVALID_PID &&
|
|
|
|
"set_child_group called with JOB_CONTROL before job pgid determined!");
|
2018-03-05 10:17:26 +08:00
|
|
|
|
2017-12-26 17:41:04 +08:00
|
|
|
// The parent sets the child's group. This incurs the well-known unavoidable race with the
|
|
|
|
// child exiting, so ignore ESRCH and EPERM (in case the pid was recycled).
|
2018-06-18 11:26:48 +08:00
|
|
|
// Additionally ignoring EACCES. See #4715 and #4884.
|
2017-12-26 17:41:04 +08:00
|
|
|
if (setpgid(child_pid, j->pgid) < 0) {
|
2018-06-18 11:26:48 +08:00
|
|
|
if (errno != ESRCH && errno != EPERM && errno != EACCES) {
|
2017-12-26 17:41:04 +08:00
|
|
|
safe_perror("setpgid");
|
2018-02-15 09:08:12 +08:00
|
|
|
return false;
|
2019-05-05 18:09:25 +08:00
|
|
|
} else {
|
2018-06-18 11:26:48 +08:00
|
|
|
// Just in case it's ever not right to ignore the setpgid call, (i.e. if this
|
|
|
|
// ever leads to a terminal hang due if both this setpgid call AND posix_spawn's
|
|
|
|
// internal setpgid calls failed), write to the debug log so a future developer
|
|
|
|
// doesn't go crazy trying to track this down.
|
2019-05-05 18:09:25 +08:00
|
|
|
debug(2, "Error %d while calling setpgid for child %d (probably harmless)", errno,
|
|
|
|
child_pid);
|
2018-06-18 11:26:48 +08:00
|
|
|
}
|
2017-12-26 17:41:04 +08:00
|
|
|
}
|
2016-05-03 12:28:06 +08:00
|
|
|
} else {
|
2017-04-23 22:12:13 +08:00
|
|
|
j->pgid = getpgrp();
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2018-02-15 09:08:12 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-02 01:57:09 +08:00
|
|
|
int child_setup_process(pid_t new_termowner, bool is_forked, const dup2_list_t &dup2s) {
|
2019-06-23 06:10:54 +08:00
|
|
|
// Note we are called in a forked child.
|
2019-01-29 06:35:56 +08:00
|
|
|
for (const auto &act : dup2s.get_actions()) {
|
|
|
|
int err = act.target < 0 ? close(act.src) : dup2(act.src, act.target);
|
|
|
|
if (err < 0) {
|
2019-07-01 15:47:10 +08:00
|
|
|
if (is_forked) {
|
2019-06-23 06:10:54 +08:00
|
|
|
debug_safe(4, "redirect_in_child_after_fork failed in child_setup_process");
|
2019-01-29 06:35:56 +08:00
|
|
|
exit_without_destructors(1);
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2019-01-29 06:35:56 +08:00
|
|
|
return err;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2019-07-02 01:57:09 +08:00
|
|
|
if (new_termowner != INVALID_PID) {
|
2019-06-23 03:33:06 +08:00
|
|
|
// Assign the terminal within the child to avoid the well-known race between tcsetgrp() in
|
|
|
|
// the parent and the child executing. We are not interested in error handling here, except
|
|
|
|
// we try to avoid this for non-terminals; in particular pipelines often make non-terminal
|
|
|
|
// stdin.
|
|
|
|
if (isatty(STDIN_FILENO)) {
|
2019-06-29 02:48:33 +08:00
|
|
|
// Ensure this doesn't send us to the background (see #5963)
|
|
|
|
signal(SIGTTIN, SIG_IGN);
|
|
|
|
signal(SIGTTOU, SIG_IGN);
|
2019-07-02 01:57:09 +08:00
|
|
|
(void)tcsetpgrp(STDIN_FILENO, new_termowner);
|
2019-06-23 03:33:06 +08:00
|
|
|
}
|
|
|
|
}
|
2019-06-29 02:48:33 +08:00
|
|
|
// Set the handling for job control signals back to the default.
|
|
|
|
// Do this after any tcsetpgrp call so that we swallow SIGTTIN.
|
|
|
|
signal_reset_handlers();
|
2012-11-19 08:30:30 +08:00
|
|
|
return 0;
|
2012-02-29 07:11:46 +08:00
|
|
|
}
|
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
/// This function is a wrapper around fork. If the fork calls fails with EAGAIN, it is retried
|
|
|
|
/// FORK_LAPS times, with a very slight delay between each lap. If fork fails even then, the process
|
|
|
|
/// will exit with an error message.
|
|
|
|
pid_t execute_fork(bool wait_for_threads_to_die) {
|
2012-02-29 07:11:46 +08:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
if (wait_for_threads_to_die || JOIN_THREADS_BEFORE_FORK) {
|
|
|
|
// Make sure we have no outstanding threads before we fork. This is a pretty sketchy thing
|
|
|
|
// to do here, both because exec.cpp shouldn't have to know about iothreads, and because the
|
|
|
|
// completion handlers may do unexpected things.
|
2017-07-30 01:40:07 +08:00
|
|
|
debug_safe(4, "waiting for threads to drain.");
|
2012-02-29 07:11:46 +08:00
|
|
|
iothread_drain_all();
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
pid_t pid;
|
|
|
|
struct timespec pollint;
|
|
|
|
int i;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
for (i = 0; i < FORK_LAPS; i++) {
|
2012-11-19 08:30:30 +08:00
|
|
|
pid = fork();
|
2016-05-03 12:28:06 +08:00
|
|
|
if (pid >= 0) {
|
2012-11-19 08:30:30 +08:00
|
|
|
return pid;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
if (errno != EAGAIN) {
|
2012-11-19 08:30:30 +08:00
|
|
|
break;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
pollint.tv_sec = 0;
|
|
|
|
pollint.tv_nsec = FORK_SLEEP_TIME;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
// Don't sleep on the final lap - sleeping might change the value of errno, which will break
|
|
|
|
// the error reporting below.
|
|
|
|
if (i != FORK_LAPS - 1) {
|
2012-11-19 08:30:30 +08:00
|
|
|
nanosleep(&pollint, NULL);
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
debug_safe(0, FORK_ERROR);
|
2013-01-10 09:06:20 +08:00
|
|
|
safe_perror("fork");
|
2012-11-19 08:30:30 +08:00
|
|
|
FATAL_EXIT();
|
2012-03-04 18:35:30 +08:00
|
|
|
return 0;
|
2012-02-29 07:11:46 +08:00
|
|
|
}
|
2012-08-15 15:57:56 +08:00
|
|
|
|
|
|
|
#if FISH_USE_POSIX_SPAWN
|
2016-05-03 12:28:06 +08:00
|
|
|
bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr,
|
2019-01-29 16:34:38 +08:00
|
|
|
posix_spawn_file_actions_t *actions, const job_t *j,
|
|
|
|
const dup2_list_t &dup2s) {
|
2016-05-03 12:28:06 +08:00
|
|
|
// Initialize the output.
|
|
|
|
if (posix_spawnattr_init(attr) != 0) {
|
2012-08-15 15:57:56 +08:00
|
|
|
return false;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
if (posix_spawn_file_actions_init(actions) != 0) {
|
2012-08-15 15:57:56 +08:00
|
|
|
posix_spawnattr_destroy(attr);
|
|
|
|
return false;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2017-04-23 18:58:01 +08:00
|
|
|
bool should_set_process_group_id = false;
|
|
|
|
int desired_process_group_id = 0;
|
2019-06-24 03:39:29 +08:00
|
|
|
if (j->wants_job_control()) {
|
2017-04-23 18:58:01 +08:00
|
|
|
should_set_process_group_id = true;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2017-04-23 22:56:33 +08:00
|
|
|
// set_child_group puts each job into its own process group
|
|
|
|
// do the same here if there is no PGID yet (i.e. PGID == -2)
|
2017-04-23 18:58:01 +08:00
|
|
|
desired_process_group_id = j->pgid;
|
2018-10-02 11:55:18 +08:00
|
|
|
if (desired_process_group_id == INVALID_PID) {
|
2017-04-23 22:56:33 +08:00
|
|
|
desired_process_group_id = 0;
|
|
|
|
}
|
2012-08-15 15:57:56 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
// Set the handling for job control signals back to the default.
|
2012-11-19 08:30:30 +08:00
|
|
|
bool reset_signal_handlers = true;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
// Remove all signal blocks.
|
2012-11-19 08:30:30 +08:00
|
|
|
bool reset_sigmask = true;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
// Set our flags.
|
2012-08-15 15:57:56 +08:00
|
|
|
short flags = 0;
|
2016-05-03 12:28:06 +08:00
|
|
|
if (reset_signal_handlers) flags |= POSIX_SPAWN_SETSIGDEF;
|
|
|
|
if (reset_sigmask) flags |= POSIX_SPAWN_SETSIGMASK;
|
2017-04-23 18:58:01 +08:00
|
|
|
if (should_set_process_group_id) flags |= POSIX_SPAWN_SETPGROUP;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-08-15 15:57:56 +08:00
|
|
|
int err = 0;
|
2016-05-03 12:28:06 +08:00
|
|
|
if (!err) err = posix_spawnattr_setflags(attr, flags);
|
2012-08-15 15:57:56 +08:00
|
|
|
|
2017-04-23 18:58:01 +08:00
|
|
|
if (!err && should_set_process_group_id)
|
|
|
|
err = posix_spawnattr_setpgroup(attr, desired_process_group_id);
|
2012-08-16 08:32:57 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
// Everybody gets default handlers.
|
|
|
|
if (!err && reset_signal_handlers) {
|
2012-08-15 15:57:56 +08:00
|
|
|
sigset_t sigdefault;
|
|
|
|
get_signals_with_handlers(&sigdefault);
|
|
|
|
err = posix_spawnattr_setsigdefault(attr, &sigdefault);
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
// No signals blocked.
|
2012-08-15 15:57:56 +08:00
|
|
|
sigset_t sigmask;
|
|
|
|
sigemptyset(&sigmask);
|
2016-05-03 12:28:06 +08:00
|
|
|
if (!err && reset_sigmask) err = posix_spawnattr_setsigmask(attr, &sigmask);
|
|
|
|
|
2019-01-29 16:34:38 +08:00
|
|
|
// Apply our dup2s.
|
|
|
|
for (const auto &act : dup2s.get_actions()) {
|
|
|
|
if (err) break;
|
|
|
|
if (act.target < 0) {
|
|
|
|
err = posix_spawn_file_actions_addclose(actions, act.src);
|
|
|
|
} else {
|
|
|
|
err = posix_spawn_file_actions_adddup2(actions, act.src, act.target);
|
2012-08-15 15:57:56 +08:00
|
|
|
}
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
// Clean up on error.
|
|
|
|
if (err) {
|
2012-08-15 15:57:56 +08:00
|
|
|
posix_spawnattr_destroy(attr);
|
|
|
|
posix_spawn_file_actions_destroy(actions);
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
return !err;
|
2012-08-15 15:57:56 +08:00
|
|
|
}
|
2016-05-03 12:28:06 +08:00
|
|
|
#endif // FISH_USE_POSIX_SPAWN
|
2012-08-15 15:57:56 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
void safe_report_exec_error(int err, const char *actual_cmd, const char *const *argv,
|
|
|
|
const char *const *envv) {
|
2012-11-19 08:30:30 +08:00
|
|
|
debug_safe(0, "Failed to execute process '%s'. Reason:", actual_cmd);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
switch (err) {
|
|
|
|
case E2BIG: {
|
2012-11-19 16:31:03 +08:00
|
|
|
char sz1[128], sz2[128];
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2012-11-19 16:31:03 +08:00
|
|
|
long arg_max = -1;
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2012-11-19 16:31:03 +08:00
|
|
|
size_t sz = 0;
|
2016-05-03 12:28:06 +08:00
|
|
|
const char *const *p;
|
|
|
|
for (p = argv; *p; p++) {
|
2019-03-13 06:07:07 +08:00
|
|
|
sz += std::strlen(*p) + 1;
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
for (p = envv; *p; p++) {
|
2019-03-13 06:07:07 +08:00
|
|
|
sz += std::strlen(*p) + 1;
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2012-11-19 16:31:03 +08:00
|
|
|
format_size_safe(sz1, sz);
|
|
|
|
arg_max = sysconf(_SC_ARG_MAX);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
if (arg_max > 0) {
|
2015-08-09 01:35:17 +08:00
|
|
|
format_size_safe(sz2, static_cast<unsigned long long>(arg_max));
|
2016-05-03 12:28:06 +08:00
|
|
|
debug_safe(0,
|
|
|
|
"The total size of the argument and environment lists %s exceeds the "
|
|
|
|
"operating system limit of %s.",
|
|
|
|
sz1, sz2);
|
|
|
|
} else {
|
|
|
|
debug_safe(0,
|
|
|
|
"The total size of the argument and environment lists (%s) exceeds the "
|
|
|
|
"operating system limit.",
|
|
|
|
sz1);
|
2012-11-19 16:31:03 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 16:31:03 +08:00
|
|
|
debug_safe(0, "Try running the command again with fewer arguments.");
|
|
|
|
break;
|
|
|
|
}
|
2012-08-15 15:57:56 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
case ENOEXEC: {
|
2013-01-10 09:06:20 +08:00
|
|
|
const char *err = safe_strerror(errno);
|
2012-11-19 16:31:03 +08:00
|
|
|
debug_safe(0, "exec: %s", err);
|
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
debug_safe(0,
|
|
|
|
"The file '%s' is marked as an executable but could not be run by the "
|
|
|
|
"operating system.",
|
|
|
|
actual_cmd);
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-11-19 16:31:03 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
case ENOENT: {
|
|
|
|
// ENOENT is returned by exec() when the path fails, but also returned by posix_spawn if
|
|
|
|
// an open file action fails. These cases appear to be impossible to distinguish. We
|
|
|
|
// address this by not using posix_spawn for file redirections, so all the ENOENTs we
|
|
|
|
// find must be errors from exec().
|
2012-11-19 16:31:03 +08:00
|
|
|
char interpreter_buff[128] = {}, *interpreter;
|
|
|
|
interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff);
|
2016-05-03 12:28:06 +08:00
|
|
|
if (interpreter && 0 != access(interpreter, X_OK)) {
|
|
|
|
debug_safe(0,
|
|
|
|
"The file '%s' specified the interpreter '%s', which is not an "
|
|
|
|
"executable command.",
|
|
|
|
actual_cmd, interpreter);
|
|
|
|
} else {
|
2012-11-19 16:31:03 +08:00
|
|
|
debug_safe(0, "The file '%s' does not exist or could not be executed.", actual_cmd);
|
|
|
|
}
|
|
|
|
break;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-08-15 15:57:56 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
case ENOMEM: {
|
2012-11-19 16:31:03 +08:00
|
|
|
debug_safe(0, "Out of memory");
|
|
|
|
break;
|
|
|
|
}
|
2012-08-15 15:57:56 +08:00
|
|
|
|
2016-05-03 12:28:06 +08:00
|
|
|
default: {
|
2013-01-10 09:06:20 +08:00
|
|
|
const char *err = safe_strerror(errno);
|
2012-11-19 16:31:03 +08:00
|
|
|
debug_safe(0, "exec: %s", err);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2019-05-30 17:54:09 +08:00
|
|
|
// FLOGF(error, L"The file '%ls' is marked as an executable but could not be run by the
|
2016-05-03 12:28:06 +08:00
|
|
|
// operating system.", p->actual_cmd);
|
2012-11-19 16:31:03 +08:00
|
|
|
break;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2012-08-15 15:57:56 +08:00
|
|
|
}
|