aboutsummaryrefslogtreecommitdiff
path: root/support/support-open-dev-null-range.c
diff options
context:
space:
mode:
Diffstat (limited to 'support/support-open-dev-null-range.c')
-rw-r--r--support/support-open-dev-null-range.c134
1 files changed, 134 insertions, 0 deletions
diff --git a/support/support-open-dev-null-range.c b/support/support-open-dev-null-range.c
new file mode 100644
index 0000000000..80d9dba504
--- /dev/null
+++ b/support/support-open-dev-null-range.c
@@ -0,0 +1,134 @@
+/* Return a range of open file descriptors.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <support/support.h>
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+
+static void
+increase_nofile (void)
+{
+ struct rlimit rl;
+ if (getrlimit (RLIMIT_NOFILE, &rl) == -1)
+ FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m");
+
+ rl.rlim_cur += 128;
+
+ if (setrlimit (RLIMIT_NOFILE, &rl) == 1)
+ FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m");
+}
+
+static int
+open_dev_null (int flags, mode_t mode)
+{
+ int fd = open64 ("/dev/null", flags, mode);
+ if (fd > 0)
+ return fd;
+
+ if (fd < 0 && errno != EMFILE)
+ FAIL_EXIT1 ("open64 (\"/dev/null\", 0x%x, 0%o): %m", flags, mode);
+
+ increase_nofile ();
+
+ return xopen ("/dev/null", flags, mode);
+}
+
+struct range
+{
+ int lowfd;
+ size_t len;
+};
+
+struct range_list
+{
+ size_t total;
+ size_t used;
+ struct range *ranges;
+};
+
+static void
+range_init (struct range_list *r)
+{
+ r->total = 8;
+ r->used = 0;
+ r->ranges = xmalloc (r->total * sizeof (struct range));
+}
+
+static void
+range_add (struct range_list *r, int lowfd, size_t len)
+{
+ if (r->used == r->total)
+ {
+ r->total *= 2;
+ r->ranges = xrealloc (r->ranges, r->total * sizeof (struct range));
+ }
+ r->ranges[r->used].lowfd = lowfd;
+ r->ranges[r->used].len = len;
+ r->used++;
+}
+
+static void
+range_close (struct range_list *r)
+{
+ for (size_t i = 0; i < r->used; i++)
+ {
+ int minfd = r->ranges[i].lowfd;
+ int maxfd = r->ranges[i].lowfd + r->ranges[i].len;
+ for (int fd = minfd; fd < maxfd; fd++)
+ xclose (fd);
+ }
+ free (r->ranges);
+}
+
+int
+support_open_dev_null_range (int num, int flags, mode_t mode)
+{
+ /* We keep track of the ranges that hit an already opened descriptor, so
+ we close them after we get a working range. */
+ struct range_list rl;
+ range_init (&rl);
+
+ int lowfd = open_dev_null (flags, mode);
+ int prevfd = lowfd;
+ while (true)
+ {
+ int i = 1;
+ for (; i < num; i++)
+ {
+ int fd = open_dev_null (flags, mode);
+ if (fd != lowfd + i)
+ {
+ range_add (&rl, lowfd, prevfd - lowfd + 1);
+
+ prevfd = lowfd = fd;
+ break;
+ }
+ prevfd = fd;
+ }
+ if (i == num)
+ break;
+ }
+
+ range_close (&rl);
+
+ return lowfd;
+}