restyle postfork module to match project style

Reduces lint errors from 37 to 20 (-46%). Line count from 670 to 566 (-15%).

Another step in resolving issue #2902.
This commit is contained in:
Kurtis Rader
2016-05-02 21:28:06 -07:00
parent 8d6b88eb5d
commit dc8d31a12a
2 changed files with 222 additions and 326 deletions

View File

@@ -1,10 +1,6 @@
/** \file postfork.cpp // Functions that we may safely call after fork().
Functions that we may safely call after fork().
*/
#include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -15,67 +11,61 @@
#endif #endif
#include "common.h" #include "common.h"
#include "proc.h"
#include "wutil.h" // IWYU pragma: keep
#include "signal.h"
#include "postfork.h"
#include "iothread.h"
#include "exec.h" #include "exec.h"
#include "io.h" #include "io.h"
#include "iothread.h"
#include "postfork.h"
#include "proc.h"
#include "signal.h"
#include "wutil.h" // IWYU pragma: keep
#ifndef JOIN_THREADS_BEFORE_FORK #ifndef JOIN_THREADS_BEFORE_FORK
#define JOIN_THREADS_BEFORE_FORK 0 #define JOIN_THREADS_BEFORE_FORK 0
#endif #endif
/** The number of times to try to call fork() before giving up */ /// The number of times to try to call fork() before giving up.
#define FORK_LAPS 5 #define FORK_LAPS 5
/** The number of nanoseconds to sleep between attempts to call fork() */ /// The number of nanoseconds to sleep between attempts to call fork().
#define FORK_SLEEP_TIME 1000000 #define FORK_SLEEP_TIME 1000000
/** Base open mode to pass to calls to open */ /// Base open mode to pass to calls to open.
#define OPEN_MASK 0666 #define OPEN_MASK 0666
/** fork error message */ /// Fork error message.
#define FORK_ERROR "Could not create child process - exiting" #define FORK_ERROR "Could not create child process - exiting"
/** file redirection clobbering error message */ /// File redirection clobbering error message.
#define NOCLOB_ERROR "The file '%s' already exists" #define NOCLOB_ERROR "The file '%s' already exists"
/** file redirection error message */ /// File redirection error message.
#define FILE_ERROR "An error occurred while redirecting file '%s'" #define FILE_ERROR "An error occurred while redirecting file '%s'"
/** file descriptor redirection error message */ /// File descriptor redirection error message.
#define FD_ERROR "An error occurred while redirecting file descriptor %s" #define FD_ERROR "An error occurred while redirecting file descriptor %s"
/** pipe error */ /// Pipe error message.
#define LOCAL_PIPE_ERROR "An error occurred while setting up pipe" #define LOCAL_PIPE_ERROR "An error occurred while setting up pipe"
static bool log_redirections = false; static bool log_redirections = false;
/* Cover for debug_safe that can take an int. The format string should expect a %s */ /// Cover for debug_safe that can take an int. The format string should expect a %s.
static void debug_safe_int(int level, const char *format, int val) static void debug_safe_int(int level, const char *format, int val) {
{
char buff[128]; char buff[128];
format_long_safe(buff, val); format_long_safe(buff, val);
debug_safe(level, format, buff); debug_safe(level, format, buff);
} }
int set_child_group(job_t *j, process_t *p, int print_errors) int set_child_group(job_t *j, process_t *p, int print_errors) {
{
int res = 0; int res = 0;
if (job_get_flag(j, JOB_CONTROL)) if (job_get_flag(j, JOB_CONTROL)) {
{ if (!j->pgid) {
if (!j->pgid)
{
j->pgid = p->pid; j->pgid = p->pid;
} }
if (setpgid(p->pid, j->pgid)) if (setpgid(p->pid, j->pgid)) {
{ if (getpgid(p->pid) != j->pgid && print_errors) {
if (getpgid(p->pid) != j->pgid && print_errors)
{
char pid_buff[128]; char pid_buff[128];
char job_id_buff[128]; char job_id_buff[128];
char getpgid_buff[128]; char getpgid_buff[128];
@@ -90,29 +80,20 @@ int set_child_group(job_t *j, process_t *p, int print_errors)
narrow_string_safe(argv0, p->argv0()); narrow_string_safe(argv0, p->argv0());
narrow_string_safe(command, j->command_wcstr()); narrow_string_safe(command, j->command_wcstr());
debug_safe(1, debug_safe(
"Could not send process %s, '%s' in job %s, '%s' from group %s to group %s", 1, "Could not send process %s, '%s' in job %s, '%s' from group %s to group %s",
pid_buff, pid_buff, argv0, job_id_buff, command, getpgid_buff, job_pgid_buff);
argv0,
job_id_buff,
command,
getpgid_buff,
job_pgid_buff);
safe_perror("setpgid"); safe_perror("setpgid");
res = -1; res = -1;
} }
} }
} } else {
else
{
j->pgid = getpid(); j->pgid = getpid();
} }
if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND)) if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND)) {
{ if (tcsetpgrp(0, j->pgid) && print_errors) {
if (tcsetpgrp(0, j->pgid) && print_errors)
{
char job_id_buff[64]; char job_id_buff[64];
char command_buff[64]; char command_buff[64];
format_long_safe(job_id_buff, j->job_id); format_long_safe(job_id_buff, j->job_id);
@@ -126,73 +107,51 @@ int set_child_group(job_t *j, process_t *p, int print_errors)
return res; return res;
} }
/** /// Set up a childs io redirections. Should only be called by setup_child_process(). Does the
Set up a childs io redirections. Should only be called by /// following: First it closes any open file descriptors not related to the child by calling
setup_child_process(). Does the following: First it closes any open /// close_unused_internal_pipes() and closing the universal variable server file descriptor. It then
file descriptors not related to the child by calling /// goes on to perform all the redirections described by \c io.
close_unused_internal_pipes() and closing the universal variable ///
server file descriptor. It then goes on to perform all the /// \param io the list of IO redirections for the child
redirections described by \c io. ///
/// \return 0 on sucess, -1 on failure
\param io the list of IO redirections for the child static int handle_child_io(const io_chain_t &io_chain) {
for (size_t idx = 0; idx < io_chain.size(); idx++) {
\return 0 on sucess, -1 on failiure
*/
static int handle_child_io(const io_chain_t &io_chain)
{
for (size_t idx = 0; idx < io_chain.size(); idx++)
{
const io_data_t *io = io_chain.at(idx).get(); const io_data_t *io = io_chain.at(idx).get();
if (io->io_mode == IO_FD && io->fd == static_cast<const io_fd_t*>(io)->old_fd) if (io->io_mode == IO_FD && io->fd == static_cast<const io_fd_t *>(io)->old_fd) {
{
continue; continue;
} }
switch (io->io_mode) switch (io->io_mode) {
{ case IO_CLOSE: {
case IO_CLOSE:
{
if (log_redirections) fprintf(stderr, "%d: close %d\n", getpid(), io->fd); if (log_redirections) fprintf(stderr, "%d: close %d\n", getpid(), io->fd);
if (close(io->fd)) if (close(io->fd)) {
{
debug_safe_int(0, "Failed to close file descriptor %s", io->fd); debug_safe_int(0, "Failed to close file descriptor %s", io->fd);
safe_perror("close"); safe_perror("close");
} }
break; break;
} }
case IO_FILE: case IO_FILE: {
{ // Here we definitely do not want to set CLO_EXEC because our child needs access.
// Here we definitely do not want to set CLO_EXEC because our child needs access
CAST_INIT(const io_file_t *, io_file, io); CAST_INIT(const io_file_t *, io_file, io);
int tmp = open(io_file->filename_cstr, io_file->flags, OPEN_MASK); int tmp = open(io_file->filename_cstr, io_file->flags, OPEN_MASK);
if (tmp < 0) if (tmp < 0) {
{ if ((io_file->flags & O_EXCL) && (errno == EEXIST)) {
if ((io_file->flags & O_EXCL) &&
(errno ==EEXIST))
{
debug_safe(1, NOCLOB_ERROR, io_file->filename_cstr); debug_safe(1, NOCLOB_ERROR, io_file->filename_cstr);
} } else {
else
{
debug_safe(1, FILE_ERROR, io_file->filename_cstr); debug_safe(1, FILE_ERROR, io_file->filename_cstr);
safe_perror("open"); safe_perror("open");
} }
return -1; return -1;
} } else if (tmp != io->fd) {
else if (tmp != io->fd) // This call will sometimes fail, but that is ok, this is just a precausion.
{
/*
This call will sometimes fail, but that is ok,
this is just a precausion.
*/
close(io->fd); close(io->fd);
if (dup2(tmp, io->fd) == -1) if (dup2(tmp, io->fd) == -1) {
{ debug_safe_int(1, FD_ERROR, io->fd);
debug_safe_int(1, FD_ERROR, io->fd);
safe_perror("dup2"); safe_perror("dup2");
exec_close(tmp); exec_close(tmp);
return -1; return -1;
@@ -202,20 +161,15 @@ static int handle_child_io(const io_chain_t &io_chain)
break; break;
} }
case IO_FD: case IO_FD: {
{
int old_fd = static_cast<const io_fd_t *>(io)->old_fd; int old_fd = static_cast<const io_fd_t *>(io)->old_fd;
if (log_redirections) fprintf(stderr, "%d: fd dup %d to %d\n", getpid(), old_fd, io->fd); if (log_redirections)
fprintf(stderr, "%d: fd dup %d to %d\n", getpid(), old_fd, io->fd);
/* // This call will sometimes fail, but that is ok, this is just a precausion.
This call will sometimes fail, but that is ok,
this is just a precausion.
*/
close(io->fd); close(io->fd);
if (dup2(old_fd, io->fd) == -1) {
if (dup2(old_fd, io->fd) == -1)
{
debug_safe_int(1, FD_ERROR, io->fd); debug_safe_int(1, FD_ERROR, io->fd);
safe_perror("dup2"); safe_perror("dup2");
return -1; return -1;
@@ -224,88 +178,72 @@ static int handle_child_io(const io_chain_t &io_chain)
} }
case IO_BUFFER: case IO_BUFFER:
case IO_PIPE: case IO_PIPE: {
{
CAST_INIT(const io_pipe_t *, io_pipe, io); CAST_INIT(const io_pipe_t *, io_pipe, io);
/* If write_pipe_idx is 0, it means we're connecting to the read end (first pipe fd). If it's 1, we're connecting to the write end (second pipe fd). */ // If write_pipe_idx is 0, it means we're connecting to the read end (first pipe
// fd). If it's 1, we're connecting to the write end (second pipe fd).
unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1); unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1);
/* #if 0
debug( 0, debug( 0, L"%ls %ls on fd %d (%d %d)", write_pipe?L"write":L"read",
L"%ls %ls on fd %d (%d %d)", (io->io_mode == IO_BUFFER)?L"buffer":L"pipe", io->fd, io->pipe_fd[0],
write_pipe?L"write":L"read", io->pipe_fd[1]);
(io->io_mode == IO_BUFFER)?L"buffer":L"pipe", #endif
io->fd, if (log_redirections)
io->pipe_fd[0], fprintf(stderr, "%d: %s dup %d to %d\n", getpid(),
io->pipe_fd[1]); io->io_mode == IO_BUFFER ? "buffer" : "pipe",
*/ io_pipe->pipe_fd[write_pipe_idx], io->fd);
if (log_redirections) fprintf(stderr, "%d: %s dup %d to %d\n", getpid(), io->io_mode == IO_BUFFER ? "buffer" : "pipe", io_pipe->pipe_fd[write_pipe_idx], io->fd); if (dup2(io_pipe->pipe_fd[write_pipe_idx], io->fd) != io->fd) {
if (dup2(io_pipe->pipe_fd[write_pipe_idx], io->fd) != io->fd)
{
debug_safe(1, LOCAL_PIPE_ERROR); debug_safe(1, LOCAL_PIPE_ERROR);
safe_perror("dup2"); safe_perror("dup2");
return -1; return -1;
} }
if (io_pipe->pipe_fd[0] >= 0) if (io_pipe->pipe_fd[0] >= 0) exec_close(io_pipe->pipe_fd[0]);
exec_close(io_pipe->pipe_fd[0]); if (io_pipe->pipe_fd[1] >= 0) exec_close(io_pipe->pipe_fd[1]);
if (io_pipe->pipe_fd[1] >= 0)
exec_close(io_pipe->pipe_fd[1]);
break; break;
} }
} }
} }
return 0; return 0;
} }
int setup_child_process(job_t *j, process_t *p, const io_chain_t &io_chain) {
bool ok = true;
int setup_child_process(job_t *j, process_t *p, const io_chain_t &io_chain) if (p) {
{
bool ok=true;
if (p)
{
ok = (0 == set_child_group(j, p, 1)); ok = (0 == set_child_group(j, p, 1));
} }
if (ok) if (ok) {
{
ok = (0 == handle_child_io(io_chain)); ok = (0 == handle_child_io(io_chain));
if (p != 0 && ! ok) if (p != 0 && !ok) {
{
exit_without_destructors(1); exit_without_destructors(1);
} }
} }
/* Set the handling for job control signals back to the default. */ if (ok) {
if (ok) // Set the handling for job control signals back to the default.
{
signal_reset_handlers(); signal_reset_handlers();
} }
/* Remove all signal blocks */ signal_unblock(); // remove all signal blocks
signal_unblock();
return ok ? 0 : -1; return ok ? 0 : -1;
} }
int g_fork_count = 0; int g_fork_count = 0;
/** /// This function is a wrapper around fork. If the fork calls fails with EAGAIN, it is retried
This function is a wrapper around fork. If the fork calls fails /// FORK_LAPS times, with a very slight delay between each lap. If fork fails even then, the process
with EAGAIN, it is retried FORK_LAPS times, with a very slight /// will exit with an error message.
delay between each lap. If fork fails even then, the process will pid_t execute_fork(bool wait_for_threads_to_die) {
exit with an error message.
*/
pid_t execute_fork(bool wait_for_threads_to_die)
{
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
if (wait_for_threads_to_die || JOIN_THREADS_BEFORE_FORK) 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
/* 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. */ // to do here, both because exec.cpp shouldn't have to know about iothreads, and because the
// completion handlers may do unexpected things.
iothread_drain_all(); iothread_drain_all();
} }
@@ -315,28 +253,22 @@ pid_t execute_fork(bool wait_for_threads_to_die)
g_fork_count++; g_fork_count++;
for (i=0; i<FORK_LAPS; i++) for (i = 0; i < FORK_LAPS; i++) {
{
pid = fork(); pid = fork();
if (pid >= 0) if (pid >= 0) {
{
return pid; return pid;
} }
if (errno != EAGAIN) if (errno != EAGAIN) {
{
break; break;
} }
pollint.tv_sec = 0; pollint.tv_sec = 0;
pollint.tv_nsec = FORK_SLEEP_TIME; pollint.tv_nsec = FORK_SLEEP_TIME;
/* // Don't sleep on the final lap - sleeping might change the value of errno, which will break
Don't sleep on the final lap - sleeping might change the // the error reporting below.
value of errno, which will break the error reporting below. if (i != FORK_LAPS - 1) {
*/
if (i != FORK_LAPS-1)
{
nanosleep(&pollint, NULL); nanosleep(&pollint, NULL);
} }
} }
@@ -348,238 +280,210 @@ pid_t execute_fork(bool wait_for_threads_to_die)
} }
#if FISH_USE_POSIX_SPAWN #if FISH_USE_POSIX_SPAWN
bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p, const io_chain_t &io_chain) bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr,
{ posix_spawn_file_actions_t *actions, job_t *j, process_t *p,
/* Initialize the output */ const io_chain_t &io_chain) {
if (posix_spawnattr_init(attr) != 0) // Initialize the output.
{ if (posix_spawnattr_init(attr) != 0) {
return false; return false;
} }
if (posix_spawn_file_actions_init(actions) != 0) if (posix_spawn_file_actions_init(actions) != 0) {
{
posix_spawnattr_destroy(attr); posix_spawnattr_destroy(attr);
return false; return false;
} }
bool should_set_parent_group_id = false; bool should_set_parent_group_id = false;
int desired_parent_group_id = 0; int desired_parent_group_id = 0;
if (job_get_flag(j, JOB_CONTROL)) if (job_get_flag(j, JOB_CONTROL)) {
{
should_set_parent_group_id = true; should_set_parent_group_id = true;
// PCA: I'm quite fuzzy on process groups, // PCA: I'm quite fuzzy on process groups, but I believe that the default value of 0 means
// but I believe that the default value of 0 // that the process becomes its own group leader, which is what set_child_group did in this
// means that the process becomes its own // case. So we want this to be 0 if j->pgid is 0.
// group leader, which is what set_child_group did
// in this case. So we want this to be 0 if j->pgid is 0.
desired_parent_group_id = j->pgid; desired_parent_group_id = j->pgid;
} }
/* Set the handling for job control signals back to the default. */ // Set the handling for job control signals back to the default.
bool reset_signal_handlers = true; bool reset_signal_handlers = true;
/* Remove all signal blocks */ // Remove all signal blocks.
bool reset_sigmask = true; bool reset_sigmask = true;
/* Set our flags */ // Set our flags.
short flags = 0; short flags = 0;
if (reset_signal_handlers) if (reset_signal_handlers) flags |= POSIX_SPAWN_SETSIGDEF;
flags |= POSIX_SPAWN_SETSIGDEF; if (reset_sigmask) flags |= POSIX_SPAWN_SETSIGMASK;
if (reset_sigmask) if (should_set_parent_group_id) flags |= POSIX_SPAWN_SETPGROUP;
flags |= POSIX_SPAWN_SETSIGMASK;
if (should_set_parent_group_id)
flags |= POSIX_SPAWN_SETPGROUP;
int err = 0; int err = 0;
if (! err) if (!err) err = posix_spawnattr_setflags(attr, flags);
err = posix_spawnattr_setflags(attr, flags);
if (! err && should_set_parent_group_id) if (!err && should_set_parent_group_id)
err = posix_spawnattr_setpgroup(attr, desired_parent_group_id); err = posix_spawnattr_setpgroup(attr, desired_parent_group_id);
/* Everybody gets default handlers */ // Everybody gets default handlers.
if (! err && reset_signal_handlers) if (!err && reset_signal_handlers) {
{
sigset_t sigdefault; sigset_t sigdefault;
get_signals_with_handlers(&sigdefault); get_signals_with_handlers(&sigdefault);
err = posix_spawnattr_setsigdefault(attr, &sigdefault); err = posix_spawnattr_setsigdefault(attr, &sigdefault);
} }
/* No signals blocked */ // No signals blocked.
sigset_t sigmask; sigset_t sigmask;
sigemptyset(&sigmask); sigemptyset(&sigmask);
if (! err && reset_sigmask) if (!err && reset_sigmask) err = posix_spawnattr_setsigmask(attr, &sigmask);
err = posix_spawnattr_setsigmask(attr, &sigmask);
for (size_t idx = 0; idx < io_chain.size(); idx++) {
for (size_t idx = 0; idx < io_chain.size(); idx++)
{
const shared_ptr<const io_data_t> io = io_chain.at(idx); const shared_ptr<const io_data_t> io = io_chain.at(idx);
if (io->io_mode == IO_FD) if (io->io_mode == IO_FD) {
{
CAST_INIT(const io_fd_t *, io_fd, io.get()); CAST_INIT(const io_fd_t *, io_fd, io.get());
if (io->fd == io_fd->old_fd) if (io->fd == io_fd->old_fd) continue;
continue;
} }
switch (io->io_mode) switch (io->io_mode) {
{ case IO_CLOSE: {
case IO_CLOSE: if (!err) err = posix_spawn_file_actions_addclose(actions, io->fd);
{
if (! err)
err = posix_spawn_file_actions_addclose(actions, io->fd);
break; break;
} }
case IO_FILE: case IO_FILE: {
{
CAST_INIT(const io_file_t *, io_file, io.get()); CAST_INIT(const io_file_t *, io_file, io.get());
if (! err) if (!err)
err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr, io_file->flags /* mode */, OPEN_MASK); err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr,
io_file->flags /* mode */, OPEN_MASK);
break; break;
} }
case IO_FD: case IO_FD: {
{
CAST_INIT(const io_fd_t *, io_fd, io.get()); CAST_INIT(const io_fd_t *, io_fd, io.get());
if (! err) if (!err)
err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */, io->fd /* to */); err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */,
io->fd /* to */);
break; break;
} }
case IO_BUFFER: case IO_BUFFER:
case IO_PIPE: case IO_PIPE: {
{
CAST_INIT(const io_pipe_t *, io_pipe, io.get()); CAST_INIT(const io_pipe_t *, io_pipe, io.get());
unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1); unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1);
int from_fd = io_pipe->pipe_fd[write_pipe_idx]; int from_fd = io_pipe->pipe_fd[write_pipe_idx];
int to_fd = io->fd; int to_fd = io->fd;
if (! err) if (!err) err = posix_spawn_file_actions_adddup2(actions, from_fd, to_fd);
err = posix_spawn_file_actions_adddup2(actions, from_fd, to_fd);
if (write_pipe_idx > 0)
{
if (! err)
err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]);
if (! err)
err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[1]);
}
else
{
if (! err)
err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]);
if (write_pipe_idx > 0) {
if (!err) err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]);
if (!err) err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[1]);
} else {
if (!err) err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]);
} }
break; break;
} }
} }
} }
/* Clean up on error */ // Clean up on error.
if (err) if (err) {
{
posix_spawnattr_destroy(attr); posix_spawnattr_destroy(attr);
posix_spawn_file_actions_destroy(actions); posix_spawn_file_actions_destroy(actions);
} }
return ! err; return !err;
} }
#endif //FISH_USE_POSIX_SPAWN #endif // FISH_USE_POSIX_SPAWN
void safe_report_exec_error(int err, const char *actual_cmd, const char * const *argv, const char *const *envv) void safe_report_exec_error(int err, const char *actual_cmd, const char *const *argv,
{ const char *const *envv) {
debug_safe(0, "Failed to execute process '%s'. Reason:", actual_cmd); debug_safe(0, "Failed to execute process '%s'. Reason:", actual_cmd);
switch (err) switch (err) {
{ case E2BIG: {
case E2BIG:
{
char sz1[128], sz2[128]; char sz1[128], sz2[128];
long arg_max = -1; long arg_max = -1;
size_t sz = 0; size_t sz = 0;
const char * const *p; const char *const *p;
for (p=argv; *p; p++) for (p = argv; *p; p++) {
{ sz += strlen(*p) + 1;
sz += strlen(*p)+1;
} }
for (p=envv; *p; p++) for (p = envv; *p; p++) {
{ sz += strlen(*p) + 1;
sz += strlen(*p)+1;
} }
format_size_safe(sz1, sz); format_size_safe(sz1, sz);
arg_max = sysconf(_SC_ARG_MAX); arg_max = sysconf(_SC_ARG_MAX);
if (arg_max > 0) if (arg_max > 0) {
{
format_size_safe(sz2, static_cast<unsigned long long>(arg_max)); format_size_safe(sz2, static_cast<unsigned long long>(arg_max));
debug_safe(0, "The total size of the argument and environment lists %s exceeds the operating system limit of %s.", sz1, sz2); debug_safe(0,
} "The total size of the argument and environment lists %s exceeds the "
else "operating system limit of %s.",
{ sz1, sz2);
debug_safe(0, "The total size of the argument and environment lists (%s) exceeds the operating system limit.", sz1); } else {
debug_safe(0,
"The total size of the argument and environment lists (%s) exceeds the "
"operating system limit.",
sz1);
} }
debug_safe(0, "Try running the command again with fewer arguments."); debug_safe(0, "Try running the command again with fewer arguments.");
break; break;
} }
case ENOEXEC: case ENOEXEC: {
{
const char *err = safe_strerror(errno); const char *err = safe_strerror(errno);
debug_safe(0, "exec: %s", err); debug_safe(0, "exec: %s", err);
debug_safe(0, "The file '%s' is marked as an executable but could not be run by the operating system.", actual_cmd); debug_safe(0,
"The file '%s' is marked as an executable but could not be run by the "
"operating system.",
actual_cmd);
break; break;
} }
case ENOENT: case ENOENT: {
{ // ENOENT is returned by exec() when the path fails, but also returned by posix_spawn if
/* 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(). */ // 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().
char interpreter_buff[128] = {}, *interpreter; char interpreter_buff[128] = {}, *interpreter;
interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff); interpreter = get_interpreter(actual_cmd, interpreter_buff, sizeof interpreter_buff);
if (interpreter && 0 != access(interpreter, X_OK)) if (interpreter && 0 != access(interpreter, X_OK)) {
{ debug_safe(0,
debug_safe(0, "The file '%s' specified the interpreter '%s', which is not an executable command.", actual_cmd, interpreter); "The file '%s' specified the interpreter '%s', which is not an "
} "executable command.",
else actual_cmd, interpreter);
{ } else {
debug_safe(0, "The file '%s' does not exist or could not be executed.", actual_cmd); debug_safe(0, "The file '%s' does not exist or could not be executed.", actual_cmd);
} }
break; break;
} }
case ENOMEM: case ENOMEM: {
{
debug_safe(0, "Out of memory"); debug_safe(0, "Out of memory");
break; break;
} }
default: default: {
{
const char *err = safe_strerror(errno); const char *err = safe_strerror(errno);
debug_safe(0, "exec: %s", err); debug_safe(0, "exec: %s", err);
// debug(0, L"The file '%ls' is marked as an executable but could not be run by the operating system.", p->actual_cmd); // debug(0, L"The file '%ls' is marked as an executable but could not be run by the
// operating system.", p->actual_cmd);
break; break;
} }
} }
} }
/** Perform output from builtins. May be called from a forked child, so don't do anything that may allocate memory, etc.. */ /// Perform output from builtins. May be called from a forked child, so don't do anything that may
bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errlen) /// allocate memory, etc.
{ bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errlen) {
bool success = true; bool success = true;
if (out && outlen) if (out && outlen) {
{ if (write_loop(STDOUT_FILENO, out, outlen) < 0) {
if (write_loop(STDOUT_FILENO, out, outlen) < 0)
{
int e = errno; int e = errno;
debug_safe(0, "Error while writing to stdout"); debug_safe(0, "Error while writing to stdout");
safe_perror("write_loop"); safe_perror("write_loop");
@@ -588,10 +492,8 @@ bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errle
} }
} }
if (err && errlen) if (err && errlen) {
{ if (write_loop(STDERR_FILENO, err, errlen) < 0) {
if (write_loop(STDERR_FILENO, err, errlen) < 0)
{
success = false; success = false;
} }
} }

View File

@@ -1,7 +1,5 @@
/** \file postfork.h // Functions that we may safely call after fork(), of which there are very few. In particular we
// cannot allocate memory, since we're insane enough to call fork from a multithreaded process.
Functions that we may safely call after fork(), of which there are very few. In particular we cannot allocate memory, since we're insane enough to call fork from a multithreaded process.
*/
#ifndef FISH_POSTFORK_H #ifndef FISH_POSTFORK_H
#define FISH_POSTFORK_H #define FISH_POSTFORK_H
@@ -20,52 +18,48 @@ class io_chain_t;
class job_t; class job_t;
class process_t; class process_t;
/** /// This function should be called by both the parent process and the child right after fork() has
This function should be called by both the parent process and the /// been called. If job control is enabled, the child is put in the jobs group, and if the child is
child right after fork() has been called. If job control is /// also in the foreground, it is also given control of the terminal. When called in the parent
enabled, the child is put in the jobs group, and if the child is /// process, this function may fail, since the child might have already finished and called exit.
also in the foreground, it is also given control of the /// The parent process may safely ignore the exit status of this call.
terminal. When called in the parent process, this function may ///
fail, since the child might have already finished and called /// Returns 0 on sucess, -1 on failiure.
exit. The parent process may safely ignore the exit status of this
call.
Returns 0 on sucess, -1 on failiure.
*/
int set_child_group(job_t *j, process_t *p, int print_errors); int set_child_group(job_t *j, process_t *p, int print_errors);
/** /// Initialize a new child process. This should be called right away after forking in the child
Initialize a new child process. This should be called right away /// process. If job control is enabled for this job, the process is put in the process group of the
after forking in the child process. If job control is enabled for /// job, all signal handlers are reset, signals are unblocked (this function may only be called
this job, the process is put in the process group of the job, all /// inside the exec function, which blocks all signals), and all IO redirections and other file
signal handlers are reset, signals are unblocked (this function may /// descriptor actions are performed.
only be called inside the exec function, which blocks all signals), ///
and all IO redirections and other file descriptor actions are /// \param j the job to set up the IO for
performed. /// \param p the child process to set up
/// \param io_chain the IO chain to use
\param j the job to set up the IO for ///
\param p the child process to set up /// \return 0 on sucess, -1 on failiure. When this function returns, signals are always unblocked.
\param io_chain the IO chain to use /// On failiure, signal handlers, io redirections and process group of the process is undefined.
\return 0 on sucess, -1 on failiure. When this function returns,
signals are always unblocked. On failiure, signal handlers, io
redirections and process group of the process is undefined.
*/
int setup_child_process(job_t *j, process_t *p, const io_chain_t &io_chain); int setup_child_process(job_t *j, process_t *p, const io_chain_t &io_chain);
/* Call fork(), optionally waiting until we are no longer multithreaded. If the forked child doesn't do anything that could allocate memory, take a lock, etc. (like call exec), then it's not necessary to wait for threads to die. If the forked child may do those things, it should wait for threads to die. /// Call fork(), optionally waiting until we are no longer multithreaded. If the forked child
*/ /// doesn't do anything that could allocate memory, take a lock, etc. (like call exec), then it's
/// not necessary to wait for threads to die. If the forked child may do those things, it should
/// wait for threads to die.
pid_t execute_fork(bool wait_for_threads_to_die); pid_t execute_fork(bool wait_for_threads_to_die);
/* Perform output from builtins. Returns true on success. */ /// Perform output from builtins. Returns true on success.
bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errlen); bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errlen);
/** Report an error from failing to exec or posix_spawn a command */ /// Report an error from failing to exec or posix_spawn a command.
void safe_report_exec_error(int err, const char *actual_cmd, const char * const *argv, const char * const *envv); void safe_report_exec_error(int err, const char *actual_cmd, const char *const *argv,
const char *const *envv);
#if FISH_USE_POSIX_SPAWN #if FISH_USE_POSIX_SPAWN
/* Initializes and fills in a posix_spawnattr_t; on success, the caller should destroy it via posix_spawnattr_destroy */ /// Initializes and fills in a posix_spawnattr_t; on success, the caller should destroy it via
bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p, const io_chain_t &io_chain); /// posix_spawnattr_destroy.
bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr,
posix_spawn_file_actions_t *actions, job_t *j, process_t *p,
const io_chain_t &io_chain);
#endif #endif
#endif #endif