]> git.ipfire.org Git - thirdparty/bash.git/blame - examples/loadables/fdflags.c
bash-5.2 distribution sources and documentation
[thirdparty/bash.git] / examples / loadables / fdflags.c
CommitLineData
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
39static 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
118extern int errno;
119#endif
120
121/* FIX THIS */
122static int
123getallflags ()
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
132static int
133getflags(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
158static void
159printone(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
187static int
188parseflags(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
226static void
227setone(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
259static int
260getmaxfd ()
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
280int
281fdflags_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
346char *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. */
367struct 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};