Merge pull request #8314 from poettering/rearrange-stdio
refactor how we rearrange fds for stdin/stdout/stderr
This commit is contained in:
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user