Skip to content

Commit

Permalink
util/fdlist: new constructor for duplicated FDs
Browse files Browse the repository at this point in the history
Create a new constructor that duplicates the provided FDs and thus takes
ownership of the FDs, but does not borrow the FDs of the caller.

Signed-off-by: David Rheinsberg <[email protected]>
  • Loading branch information
dvdhrm committed Apr 11, 2024
1 parent 5006ba4 commit f5cb156
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/util/fdlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,47 @@ int fdlist_new_consume_fds(FDList **listp, const int *fds, size_t n_fds) {
return r;
}

/**
* fdlist_new_dup_fds() - create fdlist and duplicate FDs
* @listp: output for new fdlist
* @fds: FD array to duplicate
* @n_fds: array size of @fds
*
* This is the same as fdlist_new_with_fds() but duplicates the FDs. That is,
* the caller retains the FDs, and an independent copy of each FD will be owned
* by the new object.
*
* Return: 0 on success, negative error code on failure.
*/
int fdlist_new_dup_fds(FDList **listp, const int *fds, size_t n_fds) {
_c_cleanup_(fdlist_freep) FDList *list = NULL;
size_t i;
int r, *p;

r = fdlist_new_with_fds(&list, fds, n_fds);
if (r)
return error_trace(r);

p = fdlist_data(list);
for (i = 0; i < n_fds; ++i) {
p[i] = fcntl(p[i], F_DUPFD_CLOEXEC, 3);
if (p[i] < 0) {
r = -errno;
while (i > 0) {
--i;
p[i] = c_close(p[i]);
}
return error_origin(r);
}
}

list->consumed = true;

*listp = list;
list = NULL;
return 0;
}

/**
* fdlist_free() - free fdlist
* @list: fdlist to operate on, or NULL
Expand Down
1 change: 1 addition & 0 deletions src/util/fdlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct FDList {

int fdlist_new_with_fds(FDList **listp, const int *fds, size_t n_fds);
int fdlist_new_consume_fds(FDList **listp, const int *fds, size_t n_fds);
int fdlist_new_dup_fds(FDList **listp, const int *fds, size_t n_fds);
FDList *fdlist_free(FDList *list);
void fdlist_truncate(FDList *list, size_t n_fds);
int fdlist_steal(FDList *list, size_t index);
Expand Down
47 changes: 47 additions & 0 deletions src/util/test-fdlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,56 @@ static void test_consumer(void) {
l = fdlist_free(l);
}

static void test_dup(void) {
int r, prev, p[2], expected[2];
FDList *l;

/*
* Verify that the `dup' feature of FDList objects actually duplicates
* the passed FDs and closes them on destruction. We use epoll-fds as
* examples here, though any FD would work.
*
* We use the fact that FD spaces are sparse to verify that a given FD
* was actually properly closed.
*/

prev = epoll_create1(EPOLL_CLOEXEC);
c_assert(prev >= 0);

p[0] = epoll_create1(EPOLL_CLOEXEC);
c_assert(p[0] >= 0);
c_assert(p[0] == prev + 1);

p[1] = epoll_create1(EPOLL_CLOEXEC);
c_assert(p[1] >= 0);
c_assert(p[1] == prev + 2);

r = fdlist_new_dup_fds(&l, p, C_ARRAY_SIZE(p));
c_assert(!r);

expected[0] = prev + 3;
expected[1] = prev + 4;

c_assert(fdlist_count(l) == C_ARRAY_SIZE(p));
c_assert(!memcmp(fdlist_data(l), expected, sizeof(expected)));

fdlist_truncate(l, 0);

r = epoll_create1(EPOLL_CLOEXEC);
c_assert(r == prev + 3);
close(r);

close(p[1]);
close(p[0]);
close(prev);

l = fdlist_free(l);
}

int main(int argc, char **argv) {
test_setup();
test_dummy();
test_consumer();
test_dup();
return 0;
}

0 comments on commit f5cb156

Please sign in to comment.