]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/pipesz.c
Merge branch 'getwc' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / misc-utils / pipesz.c
1 /*
2 * pipesz(1) - Set or examine pipe buffer sizes.
3 *
4 * Copyright (c) 2022 Nathan Sharp
5 * Written by Nathan Sharp <nwsharp@live.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <getopt.h>
22 #include <sys/ioctl.h> /* FIONREAD */
23 #include <fcntl.h> /* F_GETPIPE_SZ F_SETPIPE_SZ */
24
25 #include "c.h"
26 #include "nls.h"
27
28 #include "closestream.h" /* close_stdout_atexit */
29 #include "optutils.h" /* err_exclusive_options */
30 #include "path.h" /* ul_path_read_s32 */
31 #include "pathnames.h" /* _PATH_PROC_PIPE_MAX_SIZE */
32 #include "strutils.h" /* strtos32_or_err strtosize_or_err */
33
34 static char opt_check = 0; /* --check */
35 static char opt_get = 0; /* --get */
36 static char opt_quiet = 0; /* --quiet */
37 static int opt_size = -1; /* --set <size> */
38 static char opt_verbose = 0; /* --verbose */
39
40 /* fallback file for default size */
41 #ifndef PIPESZ_DEFAULT_SIZE_FILE
42 #define PIPESZ_DEFAULT_SIZE_FILE _PATH_PROC_PIPE_MAX_SIZE
43 #endif
44
45 /* convenience macros, since pipesz is by default very lenient */
46 #define check(FMT...) do { \
47 if (opt_check) { \
48 err(EXIT_FAILURE, FMT); \
49 } else if (!opt_quiet) { \
50 warn(FMT); \
51 } \
52 } while (0)
53
54 #define checkx(FMT...) do { \
55 if (opt_check) { \
56 errx(EXIT_FAILURE, FMT); \
57 } else if (!opt_quiet) { \
58 warnx(FMT); \
59 } \
60 } while (0)
61
62 static void __attribute__((__noreturn__)) usage(void)
63 {
64 fputs(USAGE_HEADER, stdout);
65 fprintf(stdout, _(" %s [options] [--set <size>] [--] [command]\n"), program_invocation_short_name);
66 fprintf(stdout, _(" %s [options] --get\n"), program_invocation_short_name);
67
68 fputs(USAGE_SEPARATOR, stdout);
69 /* TRANSLATORS: 'command' refers to a program argument */
70 fputsln(_("Set or examine pipe buffer sizes and optionally execute command."), stdout);
71
72 fputs(USAGE_OPTIONS, stdout);
73 fputsln(_(" -g, --get examine pipe buffers"), stdout);
74 /* TRANSLATORS: '%s' refers to a system file */
75 fprintf(stdout,
76 _(" -s, --set <size> set pipe buffer sizes\n"
77 " size defaults to %s\n"),
78 PIPESZ_DEFAULT_SIZE_FILE);
79
80 fputs(USAGE_SEPARATOR, stdout);
81 fputsln(_(" -f, --file <path> act on a file"), stdout);
82 fputsln(_(" -n, --fd <num> act on a file descriptor"), stdout);
83 fputsln(_(" -i, --stdin act on standard input"), stdout);
84 fputsln(_(" -o, --stdout act on standard output"), stdout);
85 fputsln(_(" -e, --stderr act on standard error"), stdout);
86
87 fputs(USAGE_SEPARATOR, stdout);
88 fputsln(_(" -c, --check do not continue after an error"), stdout);
89 fputsln(_(" -q, --quiet do not warn of non-fatal errors"), stdout);
90 fputsln(_(" -v, --verbose provide detailed output"), stdout);
91
92 fputs(USAGE_SEPARATOR, stdout);
93 fprintf(stdout, USAGE_HELP_OPTIONS(20));
94
95 fprintf(stdout, USAGE_MAN_TAIL("pipesz(1)"));
96
97 exit(EXIT_SUCCESS);
98 }
99
100 /*
101 * performs F_GETPIPE_SZ and FIONREAD
102 * outputs a table row
103 */
104 static void do_get(int fd, const char *name)
105 {
106 int sz, used;
107
108 sz = fcntl(fd, F_GETPIPE_SZ);
109 if (sz < 0) {
110 /* TRANSLATORS: '%s' refers to a file */
111 check(_("cannot get pipe buffer size of %s"), name);
112 return;
113 }
114
115 if (ioctl(fd, FIONREAD, &used))
116 used = 0;
117
118 printf("%s\t%d\t%d\n", name, sz, used);
119 }
120
121 /*
122 * performs F_SETPIPE_SZ
123 */
124 static void do_set(int fd, const char *name)
125 {
126 int sz;
127
128 sz = fcntl(fd, F_SETPIPE_SZ, opt_size);
129 if (sz < 0)
130 /* TRANSLATORS: '%s' refers to a file */
131 check(_("cannot set pipe buffer size of %s"), name);
132 else if (opt_verbose)
133 /* TRANSLATORS: '%s' refers to a file, '%d' to a buffer size in bytes */
134 warnx(_("%s pipe buffer size set to %d"), name, sz);
135 }
136
137 /*
138 * does the requested operation on an fd
139 */
140 static void do_fd(int fd)
141 {
142 char name[sizeof(stringify(INT_MIN)) + 3];
143
144 sprintf(name, "fd %d", fd);
145
146 if (opt_get)
147 do_get(fd, name);
148 else
149 do_set(fd, name);
150 }
151
152 /*
153 * does the requested operation on a file
154 */
155 static void do_file(const char *path)
156 {
157 int fd;
158
159 fd = open(path, O_RDONLY | O_CLOEXEC);
160 if (fd < 0) {
161 /* TRANSLATORS: '%s' refers to a file */
162 check(_("cannot open %s"), path);
163 return;
164 }
165
166 if (opt_get)
167 do_get(fd, path);
168 else
169 do_set(fd, path);
170
171 close(fd);
172 }
173
174 /*
175 * if necessary, determines a default buffer size and places it in opt_size
176 * returns FALSE if this could not be done
177 */
178 static char set_size_default(void)
179 {
180 if (opt_size >= 0)
181 return TRUE;
182
183 if (ul_path_read_s32(NULL, &opt_size, PIPESZ_DEFAULT_SIZE_FILE)) {
184 /* TRANSLATORS: '%s' refers to a system file */
185 check(_("cannot parse %s"), PIPESZ_DEFAULT_SIZE_FILE);
186 return FALSE;
187 }
188
189 if (opt_size < 0) {
190 /* TRANSLATORS: '%s' refers to a system file */
191 checkx(_("cannot parse %s"), PIPESZ_DEFAULT_SIZE_FILE);
192 return FALSE;
193 }
194
195 return TRUE;
196 }
197
198 int main(int argc, char **argv)
199 {
200 static const char shortopts[] = "+cef:ghin:oqs:vV";
201 static const struct option longopts[] = {
202 { "check", no_argument, NULL, 'c' },
203 { "fd", required_argument, NULL, 'n' },
204 { "file", required_argument, NULL, 'f' },
205 { "get", no_argument, NULL, 'g' },
206 { "help", no_argument, NULL, 'h' },
207 { "quiet", no_argument, NULL, 'q' },
208 { "set", required_argument, NULL, 's' },
209 { "stdin", no_argument, NULL, 'i' },
210 { "stdout", no_argument, NULL, 'o' },
211 { "stderr", no_argument, NULL, 'e' },
212 { "verbose", no_argument, NULL, 'v' },
213 { "version", no_argument, NULL, 'V' },
214 { NULL, 0, NULL, 0 }
215 };
216 static const ul_excl_t excl[] = {
217 { 'g', 's' },
218 { 0 }
219 };
220
221 int c, fd, n_opt_pipe = 0, n_opt_size = 0;
222 uintmax_t sz;
223
224 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
225
226 setlocale(LC_ALL, "");
227 bindtextdomain(PACKAGE, LOCALEDIR);
228 textdomain(PACKAGE);
229 close_stdout_atexit();
230
231 /* check for --help or --version */
232 while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
233 switch (c) {
234 case 'h':
235 usage();
236 case 'V':
237 print_version(EXIT_SUCCESS);
238 }
239 }
240
241 /* gather normal options */
242 optind = 1;
243 while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
244 err_exclusive_options(c, longopts, excl, excl_st);
245
246 switch (c) {
247 case 'c':
248 opt_check = TRUE;
249 break;
250 case 'e':
251 ++n_opt_pipe;
252 break;
253 case 'f':
254 ++n_opt_pipe;
255 break;
256 case 'g':
257 opt_get = TRUE;
258 break;
259 case 'i':
260 ++n_opt_pipe;
261 break;
262 case 'n':
263 (void) strtos32_or_err(optarg, _("invalid fd argument"));
264 ++n_opt_pipe;
265 break;
266 case 'o':
267 ++n_opt_pipe;
268 break;
269 case 'q':
270 opt_quiet = TRUE;
271 break;
272 case 's':
273 sz = strtosize_or_err(optarg, _("invalid size argument"));
274 opt_size = sz >= INT_MAX ? INT_MAX : (int)sz;
275 ++n_opt_size;
276 break;
277 case 'v':
278 opt_verbose = TRUE;
279 break;
280 default:
281 errtryhelp(EXIT_FAILURE);
282 }
283 }
284
285 /* check arguments */
286 if (opt_get) {
287 if (argv[optind])
288 errx(EXIT_FAILURE, _("cannot specify a command with --get"));
289
290 /* print column headers, if requested */
291 if (opt_verbose)
292 printf("%s\t%s\t%s\n",
293 /* TRANSLATORS: a column that contains the names of files that are unix pipes */
294 _("pipe"),
295 /* TRANSLATORS: a column that contains buffer sizes in bytes */
296 _("size"),
297 /* TRANSLATORS: a column that contains an amount of data which has not been used by a program */
298 _("unread")
299 );
300
301 /* special behavior for --get */
302 if (!n_opt_pipe) {
303 do_fd(STDIN_FILENO);
304 return EXIT_SUCCESS;
305 }
306 } else {
307 if (!set_size_default())
308 goto execute_command;
309
310 if (!opt_quiet && n_opt_size > 1)
311 warnx(_("using last specified size"));
312
313 /* special behavior for --set */
314 if (!n_opt_pipe) {
315 do_fd(STDOUT_FILENO);
316 goto execute_command;
317 }
318 }
319
320 /* go through the arguments again and do the requested operations */
321 optind = 1;
322 while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1)
323 switch (c) {
324 case 'e':
325 do_fd(STDERR_FILENO);
326 break;
327 case 'f':
328 do_file(optarg);
329 break;
330 case 'i':
331 do_fd(STDIN_FILENO);
332 break;
333 case 'n':
334 /* optarg was checked before, but it's best to be safe */
335 fd = strtos32_or_err(optarg, _("invalid fd argument"));
336 do_fd(fd);
337 break;
338 case 'o':
339 do_fd(STDOUT_FILENO);
340 break;
341 }
342
343 execute_command:
344 /* exec the command, if it's present */
345 if (!argv[optind])
346 return EXIT_SUCCESS;
347
348 execvp(argv[optind], &argv[optind]);
349 errexec(argv[optind]);
350 }