]>
Commit | Line | Data |
---|---|---|
f255336a | 1 | /* Monitoring file descriptor usage. |
2b778ceb | 2 | Copyright (C) 2018-2021 Free Software Foundation, Inc. |
f255336a FW |
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 | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
f255336a FW |
18 | |
19 | #include <dirent.h> | |
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <string.h> | |
23 | #include <support/check.h> | |
c54d8649 | 24 | #include <support/descriptors.h> |
f255336a FW |
25 | #include <support/support.h> |
26 | #include <sys/stat.h> | |
27 | #include <sys/sysmacros.h> | |
28 | #include <xunistd.h> | |
29 | ||
30 | struct procfs_descriptor | |
31 | { | |
32 | int fd; | |
33 | char *link_target; | |
34 | dev_t dev; | |
35 | ino64_t ino; | |
36 | }; | |
37 | ||
38 | /* Used with qsort. */ | |
39 | static int | |
40 | descriptor_compare (const void *l, const void *r) | |
41 | { | |
42 | const struct procfs_descriptor *left = l; | |
43 | const struct procfs_descriptor *right = r; | |
44 | /* Cannot overflow due to limited file descriptor range. */ | |
45 | return left->fd - right->fd; | |
46 | } | |
47 | ||
48 | #define DYNARRAY_STRUCT descriptor_list | |
49 | #define DYNARRAY_ELEMENT struct procfs_descriptor | |
50 | #define DYNARRAY_PREFIX descriptor_list_ | |
51 | #define DYNARRAY_ELEMENT_FREE(e) free ((e)->link_target) | |
52 | #define DYNARRAY_INITIAL_SIZE 0 | |
53 | #include <malloc/dynarray-skeleton.c> | |
54 | ||
55 | struct support_descriptors | |
56 | { | |
57 | struct descriptor_list list; | |
58 | }; | |
59 | ||
60 | struct support_descriptors * | |
61 | support_descriptors_list (void) | |
62 | { | |
63 | struct support_descriptors *result = xmalloc (sizeof (*result)); | |
64 | descriptor_list_init (&result->list); | |
65 | ||
66 | DIR *fds = opendir ("/proc/self/fd"); | |
67 | if (fds == NULL) | |
68 | FAIL_EXIT1 ("opendir (\"/proc/self/fd\"): %m"); | |
69 | ||
70 | while (true) | |
71 | { | |
72 | errno = 0; | |
73 | struct dirent64 *e = readdir64 (fds); | |
74 | if (e == NULL) | |
75 | { | |
76 | if (errno != 0) | |
77 | FAIL_EXIT1 ("readdir: %m"); | |
78 | break; | |
79 | } | |
80 | ||
81 | if (e->d_name[0] == '.') | |
82 | continue; | |
83 | ||
84 | char *endptr; | |
85 | long int fd = strtol (e->d_name, &endptr, 10); | |
86 | if (*endptr != '\0' || fd < 0 || fd > INT_MAX) | |
87 | FAIL_EXIT1 ("readdir: invalid file descriptor name: /proc/self/fd/%s", | |
88 | e->d_name); | |
89 | ||
90 | /* Skip the descriptor which is used to enumerate the | |
91 | descriptors. */ | |
92 | if (fd == dirfd (fds)) | |
93 | continue; | |
94 | ||
95 | char *target; | |
96 | { | |
97 | char *path = xasprintf ("/proc/self/fd/%ld", fd); | |
98 | target = xreadlink (path); | |
99 | free (path); | |
100 | } | |
101 | struct stat64 st; | |
102 | if (fstat64 (fd, &st) != 0) | |
103 | FAIL_EXIT1 ("readdir: fstat64 (%ld) failed: %m", fd); | |
104 | ||
105 | struct procfs_descriptor *item = descriptor_list_emplace (&result->list); | |
106 | if (item == NULL) | |
107 | FAIL_EXIT1 ("descriptor_list_emplace: %m"); | |
108 | item->fd = fd; | |
109 | item->link_target = target; | |
110 | item->dev = st.st_dev; | |
111 | item->ino = st.st_ino; | |
112 | } | |
113 | ||
114 | closedir (fds); | |
115 | ||
116 | /* Perform a merge join between descrs and current. This assumes | |
117 | that the arrays are sorted by file descriptor. */ | |
118 | ||
119 | qsort (descriptor_list_begin (&result->list), | |
120 | descriptor_list_size (&result->list), | |
121 | sizeof (struct procfs_descriptor), descriptor_compare); | |
122 | ||
123 | return result; | |
124 | } | |
125 | ||
126 | void | |
127 | support_descriptors_free (struct support_descriptors *descrs) | |
128 | { | |
129 | descriptor_list_free (&descrs->list); | |
130 | free (descrs); | |
131 | } | |
132 | ||
133 | void | |
134 | support_descriptors_dump (struct support_descriptors *descrs, | |
135 | const char *prefix, FILE *fp) | |
136 | { | |
137 | struct procfs_descriptor *end = descriptor_list_end (&descrs->list); | |
138 | for (struct procfs_descriptor *d = descriptor_list_begin (&descrs->list); | |
139 | d != end; ++d) | |
140 | { | |
141 | char *quoted = support_quote_string (d->link_target); | |
142 | fprintf (fp, "%s%d: target=\"%s\" major=%lld minor=%lld ino=%lld\n", | |
143 | prefix, d->fd, quoted, | |
144 | (long long int) major (d->dev), | |
145 | (long long int) minor (d->dev), | |
146 | (long long int) d->ino); | |
147 | free (quoted); | |
148 | } | |
149 | } | |
150 | ||
151 | static void | |
152 | dump_mismatch (bool *first, | |
153 | struct support_descriptors *descrs, | |
154 | struct support_descriptors *current) | |
155 | { | |
156 | if (*first) | |
157 | *first = false; | |
158 | else | |
159 | return; | |
160 | ||
161 | puts ("error: Differences found in descriptor set"); | |
162 | puts ("Reference descriptor set:"); | |
163 | support_descriptors_dump (descrs, " ", stdout); | |
164 | puts ("Current descriptor set:"); | |
165 | support_descriptors_dump (current, " ", stdout); | |
166 | puts ("Differences:"); | |
167 | } | |
168 | ||
169 | static void | |
170 | report_closed_descriptor (bool *first, | |
171 | struct support_descriptors *descrs, | |
172 | struct support_descriptors *current, | |
173 | struct procfs_descriptor *left) | |
174 | { | |
175 | support_record_failure (); | |
176 | dump_mismatch (first, descrs, current); | |
177 | printf ("error: descriptor %d was closed\n", left->fd); | |
178 | } | |
179 | ||
180 | static void | |
181 | report_opened_descriptor (bool *first, | |
182 | struct support_descriptors *descrs, | |
183 | struct support_descriptors *current, | |
184 | struct procfs_descriptor *right) | |
185 | { | |
186 | support_record_failure (); | |
187 | dump_mismatch (first, descrs, current); | |
188 | char *quoted = support_quote_string (right->link_target); | |
189 | printf ("error: descriptor %d was opened (\"%s\")\n", right->fd, quoted); | |
190 | free (quoted); | |
191 | } | |
192 | ||
193 | void | |
194 | support_descriptors_check (struct support_descriptors *descrs) | |
195 | { | |
196 | struct support_descriptors *current = support_descriptors_list (); | |
197 | ||
198 | /* Perform a merge join between descrs and current. This assumes | |
199 | that the arrays are sorted by file descriptor. */ | |
200 | ||
201 | struct procfs_descriptor *left = descriptor_list_begin (&descrs->list); | |
202 | struct procfs_descriptor *left_end = descriptor_list_end (&descrs->list); | |
203 | struct procfs_descriptor *right = descriptor_list_begin (¤t->list); | |
204 | struct procfs_descriptor *right_end = descriptor_list_end (¤t->list); | |
205 | ||
206 | bool first = true; | |
207 | while (left != left_end && right != right_end) | |
208 | { | |
209 | if (left->fd == right->fd) | |
210 | { | |
211 | if (strcmp (left->link_target, right->link_target) != 0) | |
212 | { | |
213 | support_record_failure (); | |
214 | char *left_quoted = support_quote_string (left->link_target); | |
215 | char *right_quoted = support_quote_string (right->link_target); | |
216 | dump_mismatch (&first, descrs, current); | |
217 | printf ("error: descriptor %d changed from \"%s\" to \"%s\"\n", | |
218 | left->fd, left_quoted, right_quoted); | |
219 | free (left_quoted); | |
220 | free (right_quoted); | |
221 | } | |
222 | if (left->dev != right->dev) | |
223 | { | |
224 | support_record_failure (); | |
225 | dump_mismatch (&first, descrs, current); | |
226 | printf ("error: descriptor %d changed device" | |
227 | " from %lld:%lld to %lld:%lld\n", | |
228 | left->fd, | |
229 | (long long int) major (left->dev), | |
230 | (long long int) minor (left->dev), | |
231 | (long long int) major (right->dev), | |
232 | (long long int) minor (right->dev)); | |
233 | } | |
234 | if (left->ino != right->ino) | |
235 | { | |
236 | support_record_failure (); | |
237 | dump_mismatch (&first, descrs, current); | |
238 | printf ("error: descriptor %d changed ino from %lld to %lld\n", | |
239 | left->fd, | |
240 | (long long int) left->ino, (long long int) right->ino); | |
241 | } | |
242 | ++left; | |
243 | ++right; | |
244 | } | |
245 | else if (left->fd < right->fd) | |
246 | { | |
247 | /* Gap on the right. */ | |
248 | report_closed_descriptor (&first, descrs, current, left); | |
249 | ++left; | |
250 | } | |
251 | else | |
252 | { | |
253 | /* Gap on the left. */ | |
254 | TEST_VERIFY_EXIT (left->fd > right->fd); | |
255 | report_opened_descriptor (&first, descrs, current, right); | |
256 | ++right; | |
257 | } | |
258 | } | |
259 | ||
260 | while (left != left_end) | |
261 | { | |
262 | /* Closed descriptors (more descriptors on the left). */ | |
263 | report_closed_descriptor (&first, descrs, current, left); | |
264 | ++left; | |
265 | } | |
266 | ||
267 | while (right != right_end) | |
268 | { | |
269 | /* Opened descriptors (more descriptors on the right). */ | |
270 | report_opened_descriptor (&first, descrs, current, right); | |
271 | ++right; | |
272 | } | |
273 | ||
274 | support_descriptors_free (current); | |
275 | } |