diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | close_range.c | 142 | ||||
-rw-r--r-- | libsimple.h | 14 | ||||
-rw-r--r-- | man0/libsimple.h.0 | 3 | ||||
-rw-r--r-- | man3/libsimple_close.3 | 3 | ||||
-rw-r--r-- | man3/libsimple_close_range.3 | 74 |
6 files changed, 236 insertions, 1 deletions
@@ -85,6 +85,7 @@ OBJ =\ bindex_r.o\ callocn.o\ close.o\ + close_range.o\ cmptimespec.o\ cmptimeval.o\ difftimespec.o\ diff --git a/close_range.c b/close_range.c new file mode 100644 index 0000000..540eba0 --- /dev/null +++ b/close_range.c @@ -0,0 +1,142 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + +#ifdef __linux__ +# include <sys/syscall.h> +#endif + + +static int +uintpcmp(const void *av, const void *bv) +{ + const unsigned int *ap = av; + const unsigned int *bp = bv; + const unsigned int a = *ap; + const unsigned int b = *bp; + return a < b ? -1 : a > b; +} + +static unsigned int * +list_fds(unsigned int min, unsigned int max, size_t *count) +{ + DIR *dir; + int ignore_fd; + unsigned int *ret = NULL, *new; + size_t size = 0; + struct dirent *f; + unsigned long int fd; + char *end; + + *count = 0; + + dir = opendir("/dev/fd"); + if (!dir) + return NULL; + ignore_fd = dirfd(dir); + + errno = 0; + while ((f = readdir(dir))) { + if (!isdigit(f->d_name[0])) + continue; + fd = strtoul(f->d_name, &end, 10); + if ((int)fd == ignore_fd || (unsigned int)fd < min || (unsigned int)fd > max) + continue; + if (*end || fd > INT_MAX || (!fd && errno)) + continue; + if (*count == size) { + size += 16; + new = realloc(ret, size * sizeof(*ret)); + if (!new) + goto fail; + ret = new; + } + ret[(*count)++] = (unsigned int)fd; + } + + if (errno) { + fail: + closedir(dir); + fail_closed: + free(ret); + return NULL; + } + + if (closedir(dir)) + goto fail_closed; + + return ret; +} + +int +libsimple_close_range(unsigned int first, unsigned int last, unsigned int *next) /* TODO test */ +{ + int saved_errno; + + *next = first; + + if (first > last) { + errno = EINVAL; + return -1; + } + + if (first > INT_MAX) + return 0; + if (last > INT_MAX) + last = INT_MAX; + + saved_errno = errno; + +#if defined(__linux__) && defined(__NR_close_range) + if (!syscall(__NR_close_range, first, last, 0)) + return 0; + else if (errno != ENOSYS) + return -1; +#endif + + if (last - first > 100) { /* TODO best limit should be researched */ + unsigned int *fds; + size_t n, i; + fds = list_fds(first, last, &n); + if (!fds) + goto fallback; + qsort(fds, n, sizeof(*fds), uintpcmp); + for (i = 0; i < n; i++) { + if (close((int)fds[i]) && errno != EBADF) { + if (i + 1 < n) + *next = fds[i + 1]; + else + *next = fds[i] + (fds[i] < LIBSIMPLE_CLOSE_RANGE_MAX); + free(fds); + return -1; + } + } + free(fds); + goto out; + } + +fallback: + do { + if (close((int)first) && errno != EBADF) { + *next = first + (first < LIBSIMPLE_CLOSE_RANGE_MAX); + return -1; + } + } while (first++ < last); + +out: + *next = last + (last < LIBSIMPLE_CLOSE_RANGE_MAX); + errno = saved_errno; + return 0; +} + + +#else +#include "test.h" + +int +main(void) +{ + return 0; +} + +#endif diff --git a/libsimple.h b/libsimple.h index f4238cb..9cc0f8f 100644 --- a/libsimple.h +++ b/libsimple.h @@ -191,6 +191,20 @@ libsimple_close(int *fdp__) /** + * Close a range of file descriptors + * + * @param first The lowest file descriptor to close + * @param last The highest file descriptor to close + * @param next Output parameter for the potentially first unclosed file descriptor; may be `NULL` + * @return 0 on successful completion, -1 on failure + * @throws EINVAL If `first > last` + * @throws Any error for close(3) except EBADF + */ +int libsimple_close_range(unsigned int first, unsigned int last, unsigned int *next); +#define LIBSIMPLE_CLOSE_RANGE_MAX (~0U) + + +/** * Remove an item from a list, keeping the list ordered * * `list` must be non-void pointer to a complete type, diff --git a/man0/libsimple.h.0 b/man0/libsimple.h.0 index 36e4f71..1f92855 100644 --- a/man0/libsimple.h.0 +++ b/man0/libsimple.h.0 @@ -159,6 +159,9 @@ Flexible allocation of memory suitable for allocating arrays. .BR libsimple_close (3) Close a file and set to stored file descriptor number to mark it file as closed. .TP +.BR libsimple_close_range (3) +Close a range of file descriptors. +.TP .BR libsimple_default_failure_exit (3) Default exit value on failure. .TP diff --git a/man3/libsimple_close.3 b/man3/libsimple_close.3 index 7050899..017318c 100644 --- a/man3/libsimple_close.3 +++ b/man3/libsimple_close.3 @@ -57,4 +57,5 @@ None. None. .SH SEE ALSO -.BR close (3) +.BR close (3), +.BR libsimple_close_range (3) diff --git a/man3/libsimple_close_range.3 b/man3/libsimple_close_range.3 new file mode 100644 index 0000000..891ac3f --- /dev/null +++ b/man3/libsimple_close_range.3 @@ -0,0 +1,74 @@ +.TH LIBSIMPLE_CLOSE 3 libsimple +.SH NAME +libsimple_close_range \- close a range of file descriptors + +.SH SYNOPSIS +.nf +#include <libsimple.h> + +#define LIBSIMPLE_CLOSE_RANGE_MAX (~0U) + +int libsimple_close_range(unsigned int first, unsigned int last, unsigned int *next); +.fi +.PP +Link with +.IR \-lsimple . + +.SH DESCRIPTION +The +.BR libsimple_close_range () +function closes all file descriptors in the +range inclusive range +.RI [ *first ", " last ]. +.PP +Unless +.I next +is, +.IR NULL , +the first potentially unclosed file descriptor +will be written to +.I *next . + +.SH RETURN VALUE +The +.BR libsimple_close_range () +function returns 0 upon successful completion; +otherwise \-1 is returned (potentially partially +successful). + +.SH ERRORS +The +.BR libsimple_close_range () +function fail for the reasons specified for the +.BR close (3) +function except +.BR EBADF , +or if +.TP +.B EINVAL +.I first +is greater than +.IR last . + +.SH EXAMPLES +None. + +.SH APPLICATION USAGE +None. + +.SH RATIONALE +None. + +.SH FUTURE DIRECTIONS +None. + +.SH NOTES +None. + +.SH BUGS +None. + +.SH SEE ALSO +.BR close_range (2), +.BR close (3), +.BR libsimple_close (3) |