309 if (!opt_quiet && n_opt_size > 1) >>> CID 378501: Control flow issues (DEADCODE) >>> Execution cannot reach this statement: "warnx(dcgettext(NULL, "usin...". 310 warnx(_("using last specified size")); Signed-off-by: Karel Zak <kzak@redhat.com>
350 lines
8.5 KiB
C
350 lines
8.5 KiB
C
/*
|
|
* pipesz(1) - Set or examine pipe buffer sizes.
|
|
*
|
|
* Copyright (c) 2022 Nathan Sharp
|
|
* Written by Nathan Sharp <nwsharp@live.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it would be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <getopt.h>
|
|
#include <sys/ioctl.h> /* FIONREAD */
|
|
#include <fcntl.h> /* F_GETPIPE_SZ F_SETPIPE_SZ */
|
|
|
|
#include "c.h"
|
|
#include "nls.h"
|
|
|
|
#include "closestream.h" /* close_stdout_atexit */
|
|
#include "optutils.h" /* err_exclusive_options */
|
|
#include "path.h" /* ul_path_read_s32 */
|
|
#include "pathnames.h" /* _PATH_PROC_PIPE_MAX_SIZE */
|
|
#include "strutils.h" /* strtos32_or_err strtosize_or_err */
|
|
|
|
static char opt_check = 0; /* --check */
|
|
static char opt_get = 0; /* --get */
|
|
static char opt_quiet = 0; /* --quiet */
|
|
static int opt_size = -1; /* --set <size> */
|
|
static char opt_verbose = 0; /* --verbose */
|
|
|
|
/* fallback file for default size */
|
|
#ifndef PIPESZ_DEFAULT_SIZE_FILE
|
|
#define PIPESZ_DEFAULT_SIZE_FILE _PATH_PROC_PIPE_MAX_SIZE
|
|
#endif
|
|
|
|
/* convenience macros, since pipesz is by default very lenient */
|
|
#define check(FMT...) do { \
|
|
if (opt_check) { \
|
|
err(EXIT_FAILURE, FMT); \
|
|
} else if (!opt_quiet) { \
|
|
warn(FMT); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define checkx(FMT...) do { \
|
|
if (opt_check) { \
|
|
errx(EXIT_FAILURE, FMT); \
|
|
} else if (!opt_quiet) { \
|
|
warnx(FMT); \
|
|
} \
|
|
} while (0)
|
|
|
|
static void __attribute__((__noreturn__)) usage(void)
|
|
{
|
|
fputs(USAGE_HEADER, stdout);
|
|
printf(_(" %s [options] [--set <size>] [--] [command]\n"), program_invocation_short_name);
|
|
printf(_(" %s [options] --get\n"), program_invocation_short_name);
|
|
|
|
fputs(USAGE_SEPARATOR, stdout);
|
|
/* TRANSLATORS: 'command' refers to a program argument */
|
|
puts(_("Set or examine pipe buffer sizes and optionally execute command."));
|
|
|
|
fputs(USAGE_OPTIONS, stdout);
|
|
puts(_(" -g, --get examine pipe buffers"));
|
|
/* TRANSLATORS: '%s' refers to a system file */
|
|
printf(
|
|
_(" -s, --set <size> set pipe buffer sizes\n"
|
|
" size defaults to %s\n"),
|
|
PIPESZ_DEFAULT_SIZE_FILE);
|
|
|
|
fputs(USAGE_SEPARATOR, stdout);
|
|
puts(_(" -f, --file <path> act on a file"));
|
|
puts(_(" -n, --fd <num> act on a file descriptor"));
|
|
puts(_(" -i, --stdin act on standard input"));
|
|
puts(_(" -o, --stdout act on standard output"));
|
|
puts(_(" -e, --stderr act on standard error"));
|
|
|
|
fputs(USAGE_SEPARATOR, stdout);
|
|
puts(_(" -c, --check do not continue after an error"));
|
|
puts(_(" -q, --quiet do not warn of non-fatal errors"));
|
|
puts(_(" -v, --verbose provide detailed output"));
|
|
|
|
fputs(USAGE_SEPARATOR, stdout);
|
|
printf(USAGE_HELP_OPTIONS(20));
|
|
|
|
printf(USAGE_MAN_TAIL("pipesz(1)"));
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* performs F_GETPIPE_SZ and FIONREAD
|
|
* outputs a table row
|
|
*/
|
|
static void do_get(int fd, const char *name)
|
|
{
|
|
int sz, used;
|
|
|
|
sz = fcntl(fd, F_GETPIPE_SZ);
|
|
if (sz < 0) {
|
|
/* TRANSLATORS: '%s' refers to a file */
|
|
check(_("cannot get pipe buffer size of %s"), name);
|
|
return;
|
|
}
|
|
|
|
if (ioctl(fd, FIONREAD, &used))
|
|
used = 0;
|
|
|
|
printf("%s\t%d\t%d\n", name, sz, used);
|
|
}
|
|
|
|
/*
|
|
* performs F_SETPIPE_SZ
|
|
*/
|
|
static void do_set(int fd, const char *name)
|
|
{
|
|
int sz;
|
|
|
|
sz = fcntl(fd, F_SETPIPE_SZ, opt_size);
|
|
if (sz < 0)
|
|
/* TRANSLATORS: '%s' refers to a file */
|
|
check(_("cannot set pipe buffer size of %s"), name);
|
|
else if (opt_verbose)
|
|
/* TRANSLATORS: '%s' refers to a file, '%d' to a buffer size in bytes */
|
|
warnx(_("%s pipe buffer size set to %d"), name, sz);
|
|
}
|
|
|
|
/*
|
|
* does the requested operation on an fd
|
|
*/
|
|
static void do_fd(int fd)
|
|
{
|
|
char name[sizeof(stringify(INT_MIN)) + 3];
|
|
|
|
sprintf(name, "fd %d", fd);
|
|
|
|
if (opt_get)
|
|
do_get(fd, name);
|
|
else
|
|
do_set(fd, name);
|
|
}
|
|
|
|
/*
|
|
* does the requested operation on a file
|
|
*/
|
|
static void do_file(const char *path)
|
|
{
|
|
int fd;
|
|
|
|
fd = open(path, O_RDONLY | O_CLOEXEC);
|
|
if (fd < 0) {
|
|
/* TRANSLATORS: '%s' refers to a file */
|
|
check(_("cannot open %s"), path);
|
|
return;
|
|
}
|
|
|
|
if (opt_get)
|
|
do_get(fd, path);
|
|
else
|
|
do_set(fd, path);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
/*
|
|
* if necessary, determines a default buffer size and places it in opt_size
|
|
* returns FALSE if this could not be done
|
|
*/
|
|
static char set_size_default(void)
|
|
{
|
|
if (opt_size >= 0)
|
|
return TRUE;
|
|
|
|
if (ul_path_read_s32(NULL, &opt_size, PIPESZ_DEFAULT_SIZE_FILE)) {
|
|
/* TRANSLATORS: '%s' refers to a system file */
|
|
check(_("cannot parse %s"), PIPESZ_DEFAULT_SIZE_FILE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (opt_size < 0) {
|
|
/* TRANSLATORS: '%s' refers to a system file */
|
|
checkx(_("cannot parse %s"), PIPESZ_DEFAULT_SIZE_FILE);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
static const char shortopts[] = "+cef:ghin:oqs:vV";
|
|
static const struct option longopts[] = {
|
|
{ "check", no_argument, NULL, 'c' },
|
|
{ "fd", required_argument, NULL, 'n' },
|
|
{ "file", required_argument, NULL, 'f' },
|
|
{ "get", no_argument, NULL, 'g' },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "quiet", no_argument, NULL, 'q' },
|
|
{ "set", required_argument, NULL, 's' },
|
|
{ "stdin", no_argument, NULL, 'i' },
|
|
{ "stdout", no_argument, NULL, 'o' },
|
|
{ "stderr", no_argument, NULL, 'e' },
|
|
{ "verbose", no_argument, NULL, 'v' },
|
|
{ "version", no_argument, NULL, 'V' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
static const ul_excl_t excl[] = {
|
|
{ 'g', 's' },
|
|
{ 0 }
|
|
};
|
|
|
|
int c, fd, n_opt_pipe = 0, n_opt_size = 0;
|
|
uintmax_t sz;
|
|
|
|
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
|
|
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
textdomain(PACKAGE);
|
|
close_stdout_atexit();
|
|
|
|
/* check for --help or --version */
|
|
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
|
switch (c) {
|
|
case 'h':
|
|
usage();
|
|
case 'V':
|
|
print_version(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
/* gather normal options */
|
|
optind = 1;
|
|
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
|
err_exclusive_options(c, longopts, excl, excl_st);
|
|
|
|
switch (c) {
|
|
case 'c':
|
|
opt_check = TRUE;
|
|
break;
|
|
case 'e':
|
|
++n_opt_pipe;
|
|
break;
|
|
case 'f':
|
|
++n_opt_pipe;
|
|
break;
|
|
case 'g':
|
|
opt_get = TRUE;
|
|
break;
|
|
case 'i':
|
|
++n_opt_pipe;
|
|
break;
|
|
case 'n':
|
|
fd = strtos32_or_err(optarg, _("invalid fd argument"));
|
|
++n_opt_pipe;
|
|
break;
|
|
case 'o':
|
|
++n_opt_pipe;
|
|
break;
|
|
case 'q':
|
|
opt_quiet = TRUE;
|
|
break;
|
|
case 's':
|
|
sz = strtosize_or_err(optarg, _("invalid size argument"));
|
|
opt_size = sz >= INT_MAX ? INT_MAX : (int)sz;
|
|
++n_opt_size;
|
|
break;
|
|
case 'v':
|
|
opt_verbose = TRUE;
|
|
break;
|
|
default:
|
|
errtryhelp(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* check arguments */
|
|
if (opt_get) {
|
|
if (argv[optind])
|
|
errx(EXIT_FAILURE, _("cannot specify a command with --get"));
|
|
|
|
/* print column headers, if requested */
|
|
if (opt_verbose)
|
|
printf("%s\t%s\t%s\n",
|
|
/* TRANSLATORS: a column that contains the names of files that are unix pipes */
|
|
_("pipe"),
|
|
/* TRANSLATORS: a column that contains buffer sizes in bytes */
|
|
_("size"),
|
|
/* TRANSLATORS: a column that contains an amount of data which has not been used by a program */
|
|
_("unread")
|
|
);
|
|
|
|
/* special behavior for --get */
|
|
if (!n_opt_pipe) {
|
|
do_fd(STDIN_FILENO);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
} else {
|
|
if (!set_size_default())
|
|
goto execute_command;
|
|
|
|
if (!opt_quiet && n_opt_size > 1)
|
|
warnx(_("using last specified size"));
|
|
|
|
/* special behavior for --set */
|
|
if (!n_opt_pipe) {
|
|
do_fd(STDOUT_FILENO);
|
|
goto execute_command;
|
|
}
|
|
}
|
|
|
|
/* go through the arguments again and do the requested operations */
|
|
optind = 1;
|
|
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1)
|
|
switch (c) {
|
|
case 'e':
|
|
do_fd(STDERR_FILENO);
|
|
break;
|
|
case 'f':
|
|
do_file(optarg);
|
|
break;
|
|
case 'i':
|
|
do_fd(STDIN_FILENO);
|
|
break;
|
|
case 'n':
|
|
/* optarg was checked before, but it's best to be safe */
|
|
fd = strtos32_or_err(optarg, _("invalid fd argument"));
|
|
do_fd(fd);
|
|
break;
|
|
case 'o':
|
|
do_fd(STDOUT_FILENO);
|
|
break;
|
|
}
|
|
|
|
execute_command:
|
|
/* exec the command, if it's present */
|
|
if (!argv[optind])
|
|
return EXIT_SUCCESS;
|
|
|
|
execvp(argv[optind], &argv[optind]);
|
|
errexec(argv[optind]);
|
|
}
|