aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--close_range.c142
-rw-r--r--libsimple.h14
-rw-r--r--man0/libsimple.h.03
-rw-r--r--man3/libsimple_close.33
-rw-r--r--man3/libsimple_close_range.374
6 files changed, 236 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 63843ae..4783e37 100644
--- a/Makefile
+++ b/Makefile
@@ -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)