]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/pipesz.c
Merge branch 'getwc' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / misc-utils / pipesz.c
CommitLineData
c221578e
NS
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
34static char opt_check = 0; /* --check */
410b261c 35static char opt_get = 0; /* --get */
c221578e
NS
36static char opt_quiet = 0; /* --quiet */
37static int opt_size = -1; /* --set <size> */
38static 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
62static void __attribute__((__noreturn__)) usage(void)
63{
64 fputs(USAGE_HEADER, stdout);
bad4c729
MY
65 fprintf(stdout, _(" %s [options] [--set <size>] [--] [command]\n"), program_invocation_short_name);
66 fprintf(stdout, _(" %s [options] --get\n"), program_invocation_short_name);
c221578e
NS
67
68 fputs(USAGE_SEPARATOR, stdout);
69 /* TRANSLATORS: 'command' refers to a program argument */
f11785b5 70 fputsln(_("Set or examine pipe buffer sizes and optionally execute command."), stdout);
c221578e
NS
71
72 fputs(USAGE_OPTIONS, stdout);
f11785b5 73 fputsln(_(" -g, --get examine pipe buffers"), stdout);
c221578e 74 /* TRANSLATORS: '%s' refers to a system file */
bad4c729 75 fprintf(stdout,
410b261c
KZ
76 _(" -s, --set <size> set pipe buffer sizes\n"
77 " size defaults to %s\n"),
78 PIPESZ_DEFAULT_SIZE_FILE);
c221578e
NS
79
80 fputs(USAGE_SEPARATOR, stdout);
f11785b5
TW
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);
c221578e
NS
86
87 fputs(USAGE_SEPARATOR, stdout);
f11785b5
TW
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);
c221578e
NS
91
92 fputs(USAGE_SEPARATOR, stdout);
bad4c729 93 fprintf(stdout, USAGE_HELP_OPTIONS(20));
c221578e 94
bad4c729 95 fprintf(stdout, USAGE_MAN_TAIL("pipesz(1)"));
c221578e
NS
96
97 exit(EXIT_SUCCESS);
98}
99
100/*
101 * performs F_GETPIPE_SZ and FIONREAD
102 * outputs a table row
103 */
104static void do_get(int fd, const char *name)
105{
106 int sz, used;
410b261c 107
c221578e
NS
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 */
124static void do_set(int fd, const char *name)
125{
126 int sz;
410b261c 127
c221578e
NS
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 */
140static 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 */
155static 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 */
178static 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
198int 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 */
410b261c 232 while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
c221578e
NS
233 switch (c) {
234 case 'h':
235 usage();
236 case 'V':
237 print_version(EXIT_SUCCESS);
238 }
410b261c 239 }
c221578e
NS
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':
132422bd 263 (void) strtos32_or_err(optarg, _("invalid fd argument"));
c221578e
NS
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;
63444630 275 ++n_opt_size;
c221578e
NS
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
343execute_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}