]>
Commit | Line | Data |
---|---|---|
d233b485 CR |
1 | /* Loadable builtin to get and set file descriptor flags. */ |
2 | ||
3 | /* See Makefile for compilation details. */ | |
4 | ||
5 | /* | |
74091dd4 | 6 | Copyright (C) 2017-2022 Free Software Foundation, Inc. |
d233b485 CR |
7 | |
8 | This file is part of GNU Bash. | |
9 | Bash is free software: you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
11 | the Free Software Foundation, either version 3 of the License, or | |
12 | (at your option) any later version. | |
13 | ||
14 | Bash is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License | |
20 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
21 | */ | |
22 | ||
23 | #include <config.h> | |
24 | ||
25 | #if defined (HAVE_UNISTD_H) | |
26 | # include <unistd.h> | |
27 | #endif | |
28 | #include <fcntl.h> | |
29 | #include <errno.h> | |
30 | #include "bashansi.h" | |
31 | #include <stdio.h> | |
32 | ||
33 | #include "loadables.h" | |
34 | ||
8868edaf CR |
35 | #ifndef FD_CLOEXEC |
36 | # define FD_CLOEXEC 1 | |
37 | #endif | |
38 | ||
d233b485 CR |
39 | static const struct |
40 | { | |
41 | const char *name; | |
42 | int value; | |
43 | } file_flags[] = | |
44 | { | |
45 | #ifdef O_APPEND | |
46 | { "append", O_APPEND }, | |
8868edaf CR |
47 | #else |
48 | # define O_APPEND 0 | |
d233b485 CR |
49 | #endif |
50 | #ifdef O_ASYNC | |
51 | { "async", O_ASYNC }, | |
8868edaf CR |
52 | #else |
53 | # define O_ASYNC 0 | |
d233b485 CR |
54 | #endif |
55 | #ifdef O_SYNC | |
56 | { "sync", O_SYNC }, | |
8868edaf CR |
57 | #else |
58 | # define O_SYNC 0 | |
d233b485 CR |
59 | #endif |
60 | #ifdef O_NONBLOCK | |
61 | { "nonblock", O_NONBLOCK }, | |
8868edaf CR |
62 | #else |
63 | # define O_NONBLOCK 0 | |
d233b485 CR |
64 | #endif |
65 | #ifdef O_FSYNC | |
66 | { "fsync", O_FSYNC }, | |
8868edaf CR |
67 | #else |
68 | # define O_FSYNC 0 | |
d233b485 CR |
69 | #endif |
70 | #ifdef O_DSYNC | |
71 | { "dsync", O_DSYNC }, | |
8868edaf CR |
72 | #else |
73 | # define O_DSYNC 0 | |
d233b485 CR |
74 | #endif |
75 | #ifdef O_RSYNC | |
76 | { "rsync", O_RSYNC }, | |
8868edaf CR |
77 | #else |
78 | # define O_RSYNC 0 | |
d233b485 CR |
79 | #endif |
80 | #ifdef O_ALT_IO | |
81 | { "altio", O_ALT_IO }, | |
8868edaf CR |
82 | #else |
83 | # define O_ALT_IO 0 | |
d233b485 CR |
84 | #endif |
85 | #ifdef O_DIRECT | |
86 | { "direct", O_DIRECT }, | |
8868edaf CR |
87 | #else |
88 | # define O_DIRECT 0 | |
d233b485 CR |
89 | #endif |
90 | #ifdef O_NOATIME | |
91 | { "noatime", O_NOATIME }, | |
8868edaf CR |
92 | #else |
93 | # define O_NOATIME 0 | |
d233b485 CR |
94 | #endif |
95 | #ifdef O_NOSIGPIPE | |
96 | { "nosigpipe", O_NOSIGPIPE }, | |
8868edaf CR |
97 | #else |
98 | # define O_NOSIGPIPE 0 | |
99 | #endif | |
100 | ||
101 | #ifndef O_CLOEXEC | |
102 | # define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_FSYNC|O_DSYNC|\ | |
103 | O_RSYNC|O_ALT_IO|O_DIRECT|O_NOATIME|O_NOSIGPIPE) | |
104 | ||
74091dd4 | 105 | /* An unused bit in the file status flags word we can use to pass around the |
8868edaf CR |
106 | state of close-on-exec. */ |
107 | # define O_CLOEXEC ((~ALLFLAGS) ^ ((~ALLFLAGS) & ((~ALLFLAGS) - 1))) | |
d233b485 | 108 | #endif |
8868edaf | 109 | |
d233b485 CR |
110 | #ifdef O_CLOEXEC |
111 | { "cloexec", O_CLOEXEC }, | |
112 | #endif | |
113 | }; | |
114 | ||
115 | #define N_FLAGS (sizeof (file_flags) / sizeof (file_flags[0])) | |
116 | ||
117 | #ifndef errno | |
118 | extern int errno; | |
119 | #endif | |
120 | ||
121 | /* FIX THIS */ | |
122 | static int | |
123 | getallflags () | |
124 | { | |
125 | int i, allflags; | |
126 | ||
127 | for (i = allflags = 0; i < N_FLAGS; i++) | |
128 | allflags |= file_flags[i].value; | |
129 | return allflags; | |
130 | } | |
131 | ||
132 | static int | |
133 | getflags(int fd, int p) | |
134 | { | |
135 | int c, f; | |
136 | int allflags; | |
137 | ||
138 | if ((c = fcntl(fd, F_GETFD)) == -1) | |
139 | { | |
140 | if (p) | |
141 | builtin_error("can't get status for fd %d: %s", fd, strerror(errno)); | |
142 | return -1; | |
143 | } | |
144 | ||
145 | if ((f = fcntl(fd, F_GETFL)) == -1) | |
146 | { | |
147 | if (p) | |
148 | builtin_error("Can't get flags for fd %d: %s", fd, strerror(errno)); | |
149 | return -1; | |
150 | } | |
151 | ||
152 | if (c) | |
153 | f |= O_CLOEXEC; | |
154 | ||
155 | return f & getallflags(); | |
156 | } | |
157 | ||
158 | static void | |
159 | printone(int fd, int p, int verbose) | |
160 | { | |
161 | int f; | |
162 | size_t i; | |
163 | ||
164 | if ((f = getflags(fd, p)) == -1) | |
165 | return; | |
166 | ||
167 | printf ("%d:", fd); | |
168 | ||
169 | for (i = 0; i < N_FLAGS; i++) | |
170 | { | |
171 | if (f & file_flags[i].value) | |
172 | { | |
173 | printf ("%s%s", verbose ? "+" : "", file_flags[i].name); | |
174 | f &= ~file_flags[i].value; | |
175 | } | |
176 | else if (verbose) | |
177 | printf ( "-%s", file_flags[i].name); | |
178 | else | |
179 | continue; | |
180 | ||
181 | if (f || (verbose && i != N_FLAGS - 1)) | |
182 | putchar (','); | |
183 | } | |
184 | printf ("\n"); | |
185 | } | |
186 | ||
187 | static int | |
188 | parseflags(char *s, int *p, int *n) | |
189 | { | |
190 | int f, *v; | |
191 | size_t i; | |
192 | ||
193 | f = 0; | |
194 | *p = *n = 0; | |
195 | ||
196 | for (s = strtok(s, ","); s; s = strtok(NULL, ",")) | |
197 | { | |
198 | switch (*s) | |
199 | { | |
200 | case '+': | |
201 | v = p; | |
202 | s++; | |
203 | break; | |
204 | case '-': | |
205 | v = n; | |
206 | s++; | |
207 | break; | |
208 | default: | |
209 | v = &f; | |
210 | break; | |
211 | } | |
212 | ||
213 | for (i = 0; i < N_FLAGS; i++) | |
214 | if (strcmp(s, file_flags[i].name) == 0) | |
215 | { | |
216 | *v |= file_flags[i].value; | |
217 | break; | |
218 | } | |
219 | if (i == N_FLAGS) | |
220 | builtin_error("invalid flag `%s'", s); | |
221 | } | |
222 | ||
223 | return f; | |
224 | } | |
225 | ||
226 | static void | |
227 | setone(int fd, char *v, int verbose) | |
228 | { | |
229 | int f, n, pos, neg, cloexec; | |
230 | ||
231 | f = getflags(fd, 1); | |
232 | if (f == -1) | |
233 | return; | |
234 | ||
235 | parseflags(v, &pos, &neg); | |
236 | ||
237 | cloexec = -1; | |
8868edaf | 238 | |
d233b485 CR |
239 | if ((pos & O_CLOEXEC) && (f & O_CLOEXEC) == 0) |
240 | cloexec = FD_CLOEXEC; | |
241 | if ((neg & O_CLOEXEC) && (f & O_CLOEXEC)) | |
242 | cloexec = 0; | |
8868edaf | 243 | |
d233b485 CR |
244 | if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1) |
245 | builtin_error("can't set status for fd %d: %s", fd, strerror(errno)); | |
246 | ||
247 | pos &= ~O_CLOEXEC; | |
248 | neg &= ~O_CLOEXEC; | |
249 | f &= ~O_CLOEXEC; | |
250 | ||
251 | n = f; | |
252 | n |= pos; | |
253 | n &= ~neg; | |
254 | ||
255 | if (n != f && fcntl(fd, F_SETFL, n) == -1) | |
256 | builtin_error("can't set flags for fd %d: %s", fd, strerror(errno)); | |
257 | } | |
258 | ||
259 | static int | |
260 | getmaxfd () | |
261 | { | |
262 | int maxfd, ignore; | |
263 | ||
264 | #ifdef F_MAXFD | |
265 | maxfd = fcntl (0, F_MAXFD); | |
266 | if (maxfd > 0) | |
267 | return maxfd; | |
268 | #endif | |
269 | ||
270 | maxfd = getdtablesize (); | |
271 | if (maxfd <= 0) | |
272 | maxfd = HIGH_FD_MAX; | |
273 | for (maxfd--; maxfd > 0; maxfd--) | |
274 | if (fcntl (maxfd, F_GETFD, &ignore) != -1) | |
275 | break; | |
276 | ||
277 | return maxfd; | |
278 | } | |
279 | ||
280 | int | |
281 | fdflags_builtin (WORD_LIST *list) | |
282 | { | |
283 | int opt, maxfd, i, num, verbose, setflag; | |
284 | char *setspec; | |
285 | WORD_LIST *l; | |
286 | intmax_t inum; | |
287 | ||
288 | setflag = verbose = 0; | |
289 | reset_internal_getopt (); | |
290 | while ((opt = internal_getopt (list, "s:v")) != -1) | |
291 | { | |
292 | switch (opt) | |
293 | { | |
294 | case 's': | |
295 | setflag = 1; | |
296 | setspec = list_optarg; | |
297 | break; | |
298 | case 'v': | |
299 | verbose = 1; | |
300 | break; | |
301 | CASE_HELPOPT; | |
302 | default: | |
303 | builtin_usage (); | |
304 | return (EX_USAGE); | |
305 | } | |
306 | ||
307 | } | |
308 | list = loptend; | |
309 | ||
310 | /* Maybe we could provide some default here, but we don't yet. */ | |
311 | if (list == 0 && setflag) | |
312 | return (EXECUTION_SUCCESS); | |
313 | ||
314 | if (list == 0) | |
315 | { | |
316 | maxfd = getmaxfd (); | |
317 | if (maxfd < 0) | |
318 | { | |
319 | builtin_error ("can't get max fd: %s", strerror (errno)); | |
320 | return (EXECUTION_FAILURE); | |
321 | } | |
322 | for (i = 0; i < maxfd; i++) | |
323 | printone (i, 0, verbose); | |
324 | return (EXECUTION_SUCCESS); | |
325 | } | |
326 | ||
327 | opt = EXECUTION_SUCCESS; | |
328 | for (l = list; l; l = l->next) | |
329 | { | |
330 | if (legal_number (l->word->word, &inum) == 0 || inum < 0) | |
331 | { | |
332 | builtin_error ("%s: invalid file descriptor", l->word->word); | |
333 | opt = EXECUTION_FAILURE; | |
334 | continue; | |
335 | } | |
336 | num = inum; /* truncate to int */ | |
337 | if (setflag) | |
338 | setone (num, setspec, verbose); | |
339 | else | |
340 | printone (num, 1, verbose); | |
341 | } | |
342 | ||
343 | return (opt); | |
344 | } | |
345 | ||
346 | char *fdflags_doc[] = | |
347 | { | |
348 | "Display and modify file descriptor flags.", | |
349 | "", | |
350 | "Display or, if the -s option is supplied, set flags for each file", | |
351 | "descriptor supplied as an argument. If the -v option is supplied,", | |
352 | "the display is verbose, including each settable option name in the", | |
353 | "form of a string such as that accepted by the -s option.", | |
354 | "", | |
355 | "The -s option accepts a string with a list of flag names, each preceded", | |
356 | "by a `+' (set) or `-' (unset). Those changes are applied to each file", | |
357 | "descriptor supplied as an argument.", | |
358 | "", | |
359 | "If no file descriptor arguments are supplied, the displayed information", | |
360 | "consists of the status of flags for each of the shell's open files.", | |
361 | (char *)NULL | |
362 | }; | |
363 | ||
364 | /* The standard structure describing a builtin command. bash keeps an array | |
365 | of these structures. The flags must include BUILTIN_ENABLED so the | |
366 | builtin can be used. */ | |
367 | struct builtin fdflags_struct = { | |
368 | "fdflags", /* builtin name */ | |
369 | fdflags_builtin, /* function implementing the builtin */ | |
370 | BUILTIN_ENABLED, /* initial flags for builtin */ | |
371 | fdflags_doc, /* array of long documentation strings. */ | |
372 | "fdflags [-v] [-s flags_string] [fd ...]", /* usage synopsis; becomes short_doc */ | |
373 | 0 /* reserved for internal use */ | |
374 | }; |