2 * pipesz(1) - Set or examine pipe buffer sizes.
4 * Copyright (c) 2022 Nathan Sharp
5 * Written by Nathan Sharp <nwsharp@live.com>
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.
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.
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.
22 #include <sys/ioctl.h> /* FIONREAD */
23 #include <fcntl.h> /* F_GETPIPE_SZ F_SETPIPE_SZ */
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 */
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 */
40 /* fallback file for default size */
41 #ifndef PIPESZ_DEFAULT_SIZE_FILE
42 #define PIPESZ_DEFAULT_SIZE_FILE _PATH_PROC_PIPE_MAX_SIZE
45 /* convenience macros, since pipesz is by default very lenient */
46 #define check(FMT...) do { \
48 err(EXIT_FAILURE, FMT); \
49 } else if (!opt_quiet) { \
54 #define checkx(FMT...) do { \
56 errx(EXIT_FAILURE, FMT); \
57 } else if (!opt_quiet) { \
62 static void __attribute__((__noreturn__
)) usage(void)
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
);
68 fputs(USAGE_SEPARATOR
, stdout
);
69 /* TRANSLATORS: 'command' refers to a program argument */
70 fputs(_("Set or examine pipe buffer sizes and optionally execute command."), stdout
);
72 fputs(USAGE_OPTIONS
, stdout
);
73 fputs(_(" -g, --get examine pipe buffers"), stdout
);
74 /* TRANSLATORS: '%s' refers to a system file */
76 _(" -s, --set <size> set pipe buffer sizes\n"
77 " size defaults to %s\n"),
78 PIPESZ_DEFAULT_SIZE_FILE
);
80 fputs(USAGE_SEPARATOR
, stdout
);
81 fputs(_(" -f, --file <path> act on a file"), stdout
);
82 fputs(_(" -n, --fd <num> act on a file descriptor"), stdout
);
83 fputs(_(" -i, --stdin act on standard input"), stdout
);
84 fputs(_(" -o, --stdout act on standard output"), stdout
);
85 fputs(_(" -e, --stderr act on standard error"), stdout
);
87 fputs(USAGE_SEPARATOR
, stdout
);
88 fputs(_(" -c, --check do not continue after an error"), stdout
);
89 fputs(_(" -q, --quiet do not warn of non-fatal errors"), stdout
);
90 fputs(_(" -v, --verbose provide detailed output"), stdout
);
92 fputs(USAGE_SEPARATOR
, stdout
);
93 fprintf(stdout
, USAGE_HELP_OPTIONS(20));
95 fprintf(stdout
, USAGE_MAN_TAIL("pipesz(1)"));
101 * performs F_GETPIPE_SZ and FIONREAD
102 * outputs a table row
104 static void do_get(int fd
, const char *name
)
108 sz
= fcntl(fd
, F_GETPIPE_SZ
);
110 /* TRANSLATORS: '%s' refers to a file */
111 check(_("cannot get pipe buffer size of %s"), name
);
115 if (ioctl(fd
, FIONREAD
, &used
))
118 printf("%s\t%d\t%d\n", name
, sz
, used
);
122 * performs F_SETPIPE_SZ
124 static void do_set(int fd
, const char *name
)
128 sz
= fcntl(fd
, F_SETPIPE_SZ
, opt_size
);
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
);
138 * does the requested operation on an fd
140 static void do_fd(int fd
)
142 char name
[sizeof(stringify(INT_MIN
)) + 3];
144 sprintf(name
, "fd %d", fd
);
153 * does the requested operation on a file
155 static void do_file(const char *path
)
159 fd
= open(path
, O_RDONLY
| O_CLOEXEC
);
161 /* TRANSLATORS: '%s' refers to a file */
162 check(_("cannot open %s"), path
);
175 * if necessary, determines a default buffer size and places it in opt_size
176 * returns FALSE if this could not be done
178 static char set_size_default(void)
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
);
190 /* TRANSLATORS: '%s' refers to a system file */
191 checkx(_("cannot parse %s"), PIPESZ_DEFAULT_SIZE_FILE
);
198 int main(int argc
, char **argv
)
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' },
216 static const ul_excl_t excl
[] = {
221 int c
, fd
, n_opt_pipe
= 0, n_opt_size
= 0;
224 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
226 setlocale(LC_ALL
, "");
227 bindtextdomain(PACKAGE
, LOCALEDIR
);
229 close_stdout_atexit();
231 /* check for --help or --version */
232 while ((c
= getopt_long(argc
, argv
, shortopts
, longopts
, NULL
)) != -1) {
237 print_version(EXIT_SUCCESS
);
241 /* gather normal options */
243 while ((c
= getopt_long(argc
, argv
, shortopts
, longopts
, NULL
)) != -1) {
244 err_exclusive_options(c
, longopts
, excl
, excl_st
);
263 fd
= strtos32_or_err(optarg
, _("invalid fd argument"));
273 sz
= strtosize_or_err(optarg
, _("invalid size argument"));
274 opt_size
= sz
>= INT_MAX
? INT_MAX
: (int)sz
;
281 errtryhelp(EXIT_FAILURE
);
285 /* check arguments */
288 errx(EXIT_FAILURE
, _("cannot specify a command with --get"));
290 /* print column headers, if requested */
292 printf("%s\t%s\t%s\n",
293 /* TRANSLATORS: a column that contains the names of files that are unix pipes */
295 /* TRANSLATORS: a column that contains buffer sizes in bytes */
297 /* TRANSLATORS: a column that contains an amount of data which has not been used by a program */
301 /* special behavior for --get */
307 if (!set_size_default())
308 goto execute_command
;
310 if (!opt_quiet
&& n_opt_size
> 1)
311 warnx(_("using last specified size"));
313 /* special behavior for --set */
315 do_fd(STDOUT_FILENO
);
316 goto execute_command
;
320 /* go through the arguments again and do the requested operations */
322 while ((c
= getopt_long(argc
, argv
, shortopts
, longopts
, NULL
)) != -1)
325 do_fd(STDERR_FILENO
);
334 /* optarg was checked before, but it's best to be safe */
335 fd
= strtos32_or_err(optarg
, _("invalid fd argument"));
339 do_fd(STDOUT_FILENO
);
344 /* exec the command, if it's present */
348 execvp(argv
[optind
], &argv
[optind
]);
349 errexec(argv
[optind
]);