summaryrefslogtreecommitdiff
path: root/src/libnm-systemd-shared/src/basic/fd-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/fd-util.c')
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c118
1 files changed, 86 insertions, 32 deletions
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index cee20a9a81..4d6d01cd99 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -29,7 +29,6 @@
#include "stat-util.h"
#include "stdio-util.h"
#include "tmpfile-util.h"
-#include "util.h"
/* The maximum number of iterations in the loop to close descriptors in the fallback case
* when /proc/self/fd/ is inaccessible. */
@@ -57,11 +56,9 @@ int close_nointr(int fd) {
}
int safe_close(int fd) {
-
/*
- * Like close_nointr() but cannot fail. Guarantees errno is
- * unchanged. Is a NOP with negative fds passed, and returns
- * -1, so that it can be used in this syntax:
+ * Like close_nointr() but cannot fail. Guarantees errno is unchanged. Is a noop for negative fds,
+ * and returns -EBADF, so that it can be used in this syntax:
*
* fd = safe_close(fd);
*/
@@ -77,7 +74,7 @@ int safe_close(int fd) {
assert_se(close_nointr(fd) != -EBADF);
}
- return -1;
+ return -EBADF;
}
void safe_close_pair(int p[static 2]) {
@@ -174,12 +171,35 @@ int fd_cloexec(int fd, bool cloexec) {
return RET_NERRNO(fcntl(fd, F_SETFD, nflags));
}
+int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
+ int ret = 0, r;
+
+ assert(n_fds == 0 || fds);
+
+ for (size_t i = 0; i < n_fds; i++) {
+ if (fds[i] < 0) /* Skip gracefully over already invalidated fds */
+ continue;
+
+ r = fd_cloexec(fds[i], cloexec);
+ if (r < 0 && ret >= 0) /* Continue going, but return first error */
+ ret = r;
+ else
+ ret = 1; /* report if we did anything */
+ }
+
+ return ret;
+}
+
_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
assert(n_fdset == 0 || fdset);
- for (size_t i = 0; i < n_fdset; i++)
+ for (size_t i = 0; i < n_fdset; i++) {
+ if (fdset[i] < 0)
+ continue;
+
if (fdset[i] == fd)
return true;
+ }
return false;
}
@@ -226,7 +246,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
"Refusing to loop over %d potential fds.",
max_fd);
- for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
+ for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) {
int q;
if (fd_in_set(fd, except, n_except))
@@ -252,6 +272,10 @@ static int close_all_fds_special_case(const int except[], size_t n_except) {
if (!have_close_range)
return 0;
+ if (n_except == 1 && except[0] < 0) /* Minor optimization: if we only got one fd, and it's invalid,
+ * we got none */
+ n_except = 0;
+
switch (n_except) {
case 0:
@@ -386,7 +410,7 @@ int close_all_fds(const int except[], size_t n_except) {
return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */
FOREACH_DIRENT(de, d, return -errno) {
- int fd = -1, q;
+ int fd = -EBADF, q;
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
continue;
@@ -475,7 +499,8 @@ void cmsg_close_all(struct msghdr *mh) {
CMSG_FOREACH(cmsg, mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
- close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+ close_many(CMSG_TYPED_DATA(cmsg, int),
+ (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
}
bool fdname_is_valid(const char *s) {
@@ -604,25 +629,23 @@ int fd_move_above_stdio(int fd) {
}
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 */
+ int fd[3] = { original_input_fd, /* Put together an array of fds we work on */
+ original_output_fd,
+ original_error_fd },
+ null_fd = -EBADF, /* If we open /dev/null, we store the fd to it here */
+ copy_fd[3] = { -EBADF, -EBADF, -EBADF }, /* This contains all fds we duplicate here
+ * temporarily, and hence need to close at the end. */
+ r;
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.
+ /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors
+ * is specified as -EBADF 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 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!
*
@@ -658,7 +681,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
}
/* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */
- for (i = 0; i < 3; i++) {
+ for (int i = 0; i < 3; i++) {
if (fd[i] < 0)
fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */
@@ -674,10 +697,10 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
}
}
- /* 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++) {
+ /* 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 -EBADF. Let's now move them to the right places. This is the point of no return. */
+ for (int i = 0; i < 3; i++) {
if (fd[i] == i) {
@@ -708,7 +731,7 @@ finish:
safe_close_above_stdio(original_error_fd);
/* Close the copies we moved > 2 */
- for (i = 0; i < 3; i++)
+ for (int i = 0; i < 3; i++)
safe_close(copy_fd[i]);
/* Close our null fd, if it's > 2 */
@@ -754,6 +777,37 @@ int fd_reopen(int fd, int flags) {
return new_fd;
}
+int fd_reopen_condition(
+ int fd,
+ int flags,
+ int mask,
+ int *ret_new_fd) {
+
+ int r, new_fd;
+
+ assert(fd >= 0);
+
+ /* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
+ * flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
+ * needed, but only then. */
+
+ r = fcntl(fd, F_GETFL);
+ if (r < 0)
+ return -errno;
+
+ if ((r & mask) == (flags & mask)) {
+ *ret_new_fd = -EBADF;
+ return fd;
+ }
+
+ new_fd = fd_reopen(fd, flags);
+ if (new_fd < 0)
+ return new_fd;
+
+ *ret_new_fd = new_fd;
+ return new_fd;
+}
+
int read_nr_open(void) {
_cleanup_free_ char *nr_open = NULL;
int r;