about summary refs log tree commit diff
path: root/support/support-open-dev-null-range.c
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2021-08-24 16:12:24 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2021-08-26 17:13:45 -0300
commite814f4b04ee413a7bb3dfa43e74c8fb4abf58359 (patch)
treed83648696fae2c7a8ee71da52297bed7dafe06c6 /support/support-open-dev-null-range.c
parent5aa359d33163bde660fec9b26e23cfb93d63ecde (diff)
downloadglibc-e814f4b04ee413a7bb3dfa43e74c8fb4abf58359.tar.gz
glibc-e814f4b04ee413a7bb3dfa43e74c8fb4abf58359.tar.xz
glibc-e814f4b04ee413a7bb3dfa43e74c8fb4abf58359.zip
support: Add support_open_dev_null_range
It returns a range of file descriptor referring to the '/dev/null'
pathname.  The function takes care of restarting the open range
if a file descriptor is found within the specified range and
also increases RLIMIT_NOFILE if required.

Checked on x86_64-linux-gnu.
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;
+}