init version.
[bush.git] / examples / loadables / fdflags.c
blobaaab63dba4152699294706efca21a518965dfc25
1 /* Loadable builtin to get and set file descriptor flags. */
3 /* See Makefile for compilation details. */
5 /*
6 Copyright (C) 2017,2018,2019 Free Software Foundation, Inc.
8 This file is part of GNU Bush.
9 Bush 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.
14 Bush 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.
19 You should have received a copy of the GNU General Public License
20 along with Bush. If not, see <http://www.gnu.org/licenses/>.
23 #include <config.h>
25 #if defined (HAVE_UNISTD_H)
26 # include <unistd.h>
27 #endif
28 #include <fcntl.h>
29 #include <errno.h>
30 #include "bushansi.h"
31 #include <stdio.h>
33 #include "loadables.h"
35 #ifndef FD_CLOEXEC
36 # define FD_CLOEXEC 1
37 #endif
39 static const struct
41 const char *name;
42 int value;
43 } file_flags[] =
45 #ifdef O_APPEND
46 { "append", O_APPEND },
47 #else
48 # define O_APPEND 0
49 #endif
50 #ifdef O_ASYNC
51 { "async", O_ASYNC },
52 #else
53 # define O_ASYNC 0
54 #endif
55 #ifdef O_SYNC
56 { "sync", O_SYNC },
57 #else
58 # define O_SYNC 0
59 #endif
60 #ifdef O_NONBLOCK
61 { "nonblock", O_NONBLOCK },
62 #else
63 # define O_NONBLOCK 0
64 #endif
65 #ifdef O_FSYNC
66 { "fsync", O_FSYNC },
67 #else
68 # define O_FSYNC 0
69 #endif
70 #ifdef O_DSYNC
71 { "dsync", O_DSYNC },
72 #else
73 # define O_DSYNC 0
74 #endif
75 #ifdef O_RSYNC
76 { "rsync", O_RSYNC },
77 #else
78 # define O_RSYNC 0
79 #endif
80 #ifdef O_ALT_IO
81 { "altio", O_ALT_IO },
82 #else
83 # define O_ALT_IO 0
84 #endif
85 #ifdef O_DIRECT
86 { "direct", O_DIRECT },
87 #else
88 # define O_DIRECT 0
89 #endif
90 #ifdef O_NOATIME
91 { "noatime", O_NOATIME },
92 #else
93 # define O_NOATIME 0
94 #endif
95 #ifdef O_NOSIGPIPE
96 { "nosigpipe", O_NOSIGPIPE },
97 #else
98 # define O_NOSIGPIPE 0
99 #endif
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)
105 /* An unsed bit in the file status flags word we can use to pass around the
106 state of close-on-exec. */
107 # define O_CLOEXEC ((~ALLFLAGS) ^ ((~ALLFLAGS) & ((~ALLFLAGS) - 1)))
108 #endif
110 #ifdef O_CLOEXEC
111 { "cloexec", O_CLOEXEC },
112 #endif
115 #define N_FLAGS (sizeof (file_flags) / sizeof (file_flags[0]))
117 #ifndef errno
118 extern int errno;
119 #endif
121 /* FIX THIS */
122 static int
123 getallflags ()
125 int i, allflags;
127 for (i = allflags = 0; i < N_FLAGS; i++)
128 allflags |= file_flags[i].value;
129 return allflags;
132 static int
133 getflags(int fd, int p)
135 int c, f;
136 int allflags;
138 if ((c = fcntl(fd, F_GETFD)) == -1)
140 if (p)
141 builtin_error("can't get status for fd %d: %s", fd, strerror(errno));
142 return -1;
145 if ((f = fcntl(fd, F_GETFL)) == -1)
147 if (p)
148 builtin_error("Can't get flags for fd %d: %s", fd, strerror(errno));
149 return -1;
152 if (c)
153 f |= O_CLOEXEC;
155 return f & getallflags();
158 static void
159 printone(int fd, int p, int verbose)
161 int f;
162 size_t i;
164 if ((f = getflags(fd, p)) == -1)
165 return;
167 printf ("%d:", fd);
169 for (i = 0; i < N_FLAGS; i++)
171 if (f & file_flags[i].value)
173 printf ("%s%s", verbose ? "+" : "", file_flags[i].name);
174 f &= ~file_flags[i].value;
176 else if (verbose)
177 printf ( "-%s", file_flags[i].name);
178 else
179 continue;
181 if (f || (verbose && i != N_FLAGS - 1))
182 putchar (',');
184 printf ("\n");
187 static int
188 parseflags(char *s, int *p, int *n)
190 int f, *v;
191 size_t i;
193 f = 0;
194 *p = *n = 0;
196 for (s = strtok(s, ","); s; s = strtok(NULL, ","))
198 switch (*s)
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;
213 for (i = 0; i < N_FLAGS; i++)
214 if (strcmp(s, file_flags[i].name) == 0)
216 *v |= file_flags[i].value;
217 break;
219 if (i == N_FLAGS)
220 builtin_error("invalid flag `%s'", s);
223 return f;
226 static void
227 setone(int fd, char *v, int verbose)
229 int f, n, pos, neg, cloexec;
231 f = getflags(fd, 1);
232 if (f == -1)
233 return;
235 parseflags(v, &pos, &neg);
237 cloexec = -1;
239 if ((pos & O_CLOEXEC) && (f & O_CLOEXEC) == 0)
240 cloexec = FD_CLOEXEC;
241 if ((neg & O_CLOEXEC) && (f & O_CLOEXEC))
242 cloexec = 0;
244 if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1)
245 builtin_error("can't set status for fd %d: %s", fd, strerror(errno));
247 pos &= ~O_CLOEXEC;
248 neg &= ~O_CLOEXEC;
249 f &= ~O_CLOEXEC;
251 n = f;
252 n |= pos;
253 n &= ~neg;
255 if (n != f && fcntl(fd, F_SETFL, n) == -1)
256 builtin_error("can't set flags for fd %d: %s", fd, strerror(errno));
259 static int
260 getmaxfd ()
262 int maxfd, ignore;
264 #ifdef F_MAXFD
265 maxfd = fcntl (0, F_MAXFD);
266 if (maxfd > 0)
267 return maxfd;
268 #endif
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;
277 return maxfd;
281 fdflags_builtin (WORD_LIST *list)
283 int opt, maxfd, i, num, verbose, setflag;
284 char *setspec;
285 WORD_LIST *l;
286 intmax_t inum;
288 setflag = verbose = 0;
289 reset_internal_getopt ();
290 while ((opt = internal_getopt (list, "s:v")) != -1)
292 switch (opt)
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);
308 list = loptend;
310 /* Maybe we could provide some default here, but we don't yet. */
311 if (list == 0 && setflag)
312 return (EXECUTION_SUCCESS);
314 if (list == 0)
316 maxfd = getmaxfd ();
317 if (maxfd < 0)
319 builtin_error ("can't get max fd: %s", strerror (errno));
320 return (EXECUTION_FAILURE);
322 for (i = 0; i < maxfd; i++)
323 printone (i, 0, verbose);
324 return (EXECUTION_SUCCESS);
327 opt = EXECUTION_SUCCESS;
328 for (l = list; l; l = l->next)
330 if (legal_number (l->word->word, &inum) == 0 || inum < 0)
332 builtin_error ("%s: invalid file descriptor", l->word->word);
333 opt = EXECUTION_FAILURE;
334 continue;
336 num = inum; /* truncate to int */
337 if (setflag)
338 setone (num, setspec, verbose);
339 else
340 printone (num, 1, verbose);
343 return (opt);
346 char *fdflags_doc[] =
348 "Display and modify file descriptor flags.",
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.",
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.",
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
364 /* The standard structure describing a builtin command. bush 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 */