1/* Return a range of open file descriptors.
2 Copyright (C) 2021-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <errno.h>
20#include <fcntl.h>
21#include <support/support.h>
22#include <support/check.h>
23#include <support/xunistd.h>
24#include <stdlib.h>
25#include <sys/resource.h>
26
27static void
28increase_nofile (void)
29{
30 struct rlimit rl;
31 if (getrlimit (RLIMIT_NOFILE, rlimits: &rl) == -1)
32 FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m");
33
34 rl.rlim_cur += 128;
35
36 if (setrlimit (RLIMIT_NOFILE, rlimits: &rl) == 1)
37 FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m");
38}
39
40static int
41open_dev_null (int flags, mode_t mode)
42{
43 int fd = open64 (file: "/dev/null", oflag: flags, mode);
44 if (fd >= 0)
45 return fd;
46
47 if (fd < 0 && errno != EMFILE)
48 FAIL_EXIT1 ("open64 (\"/dev/null\", 0x%x, 0%o): %m", flags, mode);
49
50 increase_nofile ();
51
52 return xopen (path: "/dev/null", flags, mode);
53}
54
55struct range
56{
57 int lowfd;
58 size_t len;
59};
60
61struct range_list
62{
63 size_t total;
64 size_t used;
65 struct range *ranges;
66};
67
68static void
69range_init (struct range_list *r)
70{
71 r->total = 8;
72 r->used = 0;
73 r->ranges = xmalloc (n: r->total * sizeof (struct range));
74}
75
76static void
77range_add (struct range_list *r, int lowfd, size_t len)
78{
79 if (r->used == r->total)
80 {
81 r->total *= 2;
82 r->ranges = xrealloc (o: r->ranges, n: r->total * sizeof (struct range));
83 }
84 r->ranges[r->used].lowfd = lowfd;
85 r->ranges[r->used].len = len;
86 r->used++;
87}
88
89static void
90range_close (struct range_list *r)
91{
92 for (size_t i = 0; i < r->used; i++)
93 {
94 int minfd = r->ranges[i].lowfd;
95 int maxfd = r->ranges[i].lowfd + r->ranges[i].len;
96 for (int fd = minfd; fd < maxfd; fd++)
97 xclose (fd);
98 }
99 free (ptr: r->ranges);
100}
101
102int
103support_open_dev_null_range (int num, int flags, mode_t mode)
104{
105 /* We keep track of the ranges that hit an already opened descriptor, so
106 we close them after we get a working range. */
107 struct range_list rl;
108 range_init (r: &rl);
109
110 int lowfd = open_dev_null (flags, mode);
111 int prevfd = lowfd;
112 while (true)
113 {
114 int i = 1;
115 for (; i < num; i++)
116 {
117 int fd = open_dev_null (flags, mode);
118 if (fd != lowfd + i)
119 {
120 range_add (r: &rl, lowfd, len: prevfd - lowfd + 1);
121
122 prevfd = lowfd = fd;
123 break;
124 }
125 prevfd = fd;
126 }
127 if (i == num)
128 break;
129 }
130
131 range_close (r: &rl);
132
133 return lowfd;
134}
135

source code of glibc/support/support-open-dev-null-range.c