Merge pull request #8314 from poettering/rearrange-stdio

refactor how we rearrange fds for stdin/stdout/stderr
This commit is contained in:
Lennart Poettering
2018-03-02 15:42:03 +01:00
committed by GitHub
18 changed files with 236 additions and 216 deletions
+2 -5
View File
@@ -62,12 +62,9 @@ static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
char *_argv[2];
if (stdout_fd >= 0) {
/* If the fd happens to be in the right place, go along with that */
if (stdout_fd != STDOUT_FILENO &&
dup2(stdout_fd, STDOUT_FILENO) < 0)
r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
if (r < 0)
_exit(EXIT_FAILURE);
(void) fd_cloexec(STDOUT_FILENO, false);
}
if (!argv) {
+115 -6
View File
@@ -191,12 +191,6 @@ int fd_cloexec(int fd, bool cloexec) {
return 0;
}
void stdio_unset_cloexec(void) {
(void) fd_cloexec(STDIN_FILENO, false);
(void) fd_cloexec(STDOUT_FILENO, false);
(void) fd_cloexec(STDERR_FILENO, false);
}
_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
unsigned i;
@@ -615,3 +609,118 @@ int fd_move_above_stdio(int fd) {
(void) close(fd);
return copy;
}
int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) {
int fd[3] = { /* Put together an array of fds we work on */
original_input_fd,
original_output_fd,
original_error_fd
};
int r, i,
null_fd = -1, /* if we open /dev/null, we store the fd to it here */
copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */
bool null_readable, null_writable;
/* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is
* specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as
* itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be
* on.
*
* Note that if any of the passed file descriptors are > 2 they will be closed — both on success and on
* failure! Thus, callers should assume that when this function returns the input fds are invalidated.
*
* Note that when this function fails stdin/stdout/stderr might remain half set up!
*
* O_CLOEXEC is turned off for all three file descriptors (which is how it should be for
* stdin/stdout/stderr). */
null_readable = original_input_fd < 0;
null_writable = original_output_fd < 0 || original_error_fd < 0;
/* First step, open /dev/null once, if we need it */
if (null_readable || null_writable) {
/* Let's open this with O_CLOEXEC first, and convert it to non-O_CLOEXEC when we move the fd to the final position. */
null_fd = open("/dev/null", (null_readable && null_writable ? O_RDWR :
null_readable ? O_RDONLY : O_WRONLY) | O_CLOEXEC);
if (null_fd < 0) {
r = -errno;
goto finish;
}
/* If this fd is in the 0…2 range, let's move it out of it */
if (null_fd < 3) {
int copy;
copy = fcntl(null_fd, F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */
if (copy < 0) {
r = -errno;
goto finish;
}
safe_close(null_fd);
null_fd = copy;
}
}
/* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */
for (i = 0; i < 3; i++) {
if (fd[i] < 0)
fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */
else if (fd[i] != i && fd[i] < 3) {
/* This fd is in the 0…2 territory, but not at its intended place, move it out of there, so that we can work there. */
copy_fd[i] = fcntl(fd[i], F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */
if (copy_fd[i] < 0) {
r = -errno;
goto finish;
}
fd[i] = copy_fd[i];
}
}
/* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we
* have freedom to move them around. If the fds already were at the right places then the specific fds are
* -1. Let's now move them to the right places. This is the point of no return. */
for (i = 0; i < 3; i++) {
if (fd[i] == i) {
/* fd is already in place, but let's make sure O_CLOEXEC is off */
r = fd_cloexec(i, false);
if (r < 0)
goto finish;
} else {
assert(fd[i] > 2);
if (dup2(fd[i], i) < 0) { /* Turns off O_CLOEXEC on the new fd. */
r = -errno;
goto finish;
}
}
}
r = 0;
finish:
/* Close the original fds, but only if they were outside of the stdio range. Also, properly check for the same
* fd passed in multiple times. */
safe_close_above_stdio(original_input_fd);
if (original_output_fd != original_input_fd)
safe_close_above_stdio(original_output_fd);
if (original_error_fd != original_input_fd && original_error_fd != original_output_fd)
safe_close_above_stdio(original_error_fd);
/* Close the copies we moved > 2 */
for (i = 0; i < 3; i++)
safe_close(copy_fd[i]);
/* Close our null fd, if it's > 2 */
safe_close_above_stdio(null_fd);
return r;
}
+6 -1
View File
@@ -71,7 +71,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
int fd_nonblock(int fd, bool nonblock);
int fd_cloexec(int fd, bool cloexec);
void stdio_unset_cloexec(void);
int close_all_fds(const int except[], unsigned n_except);
@@ -100,3 +99,9 @@ int acquire_data_fd(const void *data, size_t size, unsigned flags);
IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
int fd_move_above_stdio(int fd);
int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd);
static inline int make_null_stdio(void) {
return rearrange_stdio(-1, -1, -1);
}
+2 -36
View File
@@ -628,9 +628,9 @@ int make_console_stdio(void) {
if (r < 0)
log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
r = make_stdio(fd);
r = rearrange_stdio(fd, fd, fd); /* This invalidates 'fd' both on success and on failure. */
if (r < 0)
return log_error_errno(r, "Failed to duplicate terminal fd: %m");
return log_error_errno(r, "Failed to make terminal stdin/stdout/stderr: %m");
reset_terminal_feature_caches();
@@ -905,40 +905,6 @@ bool on_tty(void) {
return cached_on_tty;
}
int make_stdio(int fd) {
int r = 0;
assert(fd >= 0);
if (dup2(fd, STDIN_FILENO) < 0)
r = -errno;
if (dup2(fd, STDOUT_FILENO) < 0 && r >= 0)
r = -errno;
if (dup2(fd, STDERR_FILENO) < 0 && r >= 0)
r = -errno;
safe_close_above_stdio(fd);
/* Explicitly unset O_CLOEXEC, since if fd was < 3, then dup2() was a NOP and the bit hence possibly set. */
stdio_unset_cloexec();
return r;
}
int make_null_stdio(void) {
int null_fd, r;
null_fd = open("/dev/null", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (null_fd < 0)
return -errno;
r = make_stdio(null_fd);
reset_terminal_feature_caches();
return r;
}
int getttyname_malloc(int fd, char **ret) {
size_t l = 100;
int r;
-2
View File
@@ -114,8 +114,6 @@ bool tty_is_console(const char *tty) _pure_;
int vtnr_from_tty(const char *tty);
const char *default_term_for_tty(const char *tty);
int make_stdio(int fd);
int make_null_stdio(void);
int make_console_stdio(void);
int fd_columns(int fd);