Browse Source

pipesz: add the pipesz utility

pipesz is a utility to examine and adjust the size of pipe buffers.

It uses fctnl F_GETPIPE_SZ and F_SETPIPE_SZ to examine and resize
these buffers. This functionality is unique to Linux and was added in
version 2.6.35. Minor bugfixes were made in 4.9, but these do not
obviate the use of pipesz prior to that release.

Signed-off-by: Nathan Sharp <nwsharp@live.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
pull/1694/head
Nathan Sharp 3 months ago committed by Karel Zak
parent
commit
c221578e4b
  1. 1
      .gitignore
  2. 8
      configure.ac
  3. 4
      include/pathnames.h
  4. 12
      meson.build
  5. 2
      meson_options.txt
  6. 7
      misc-utils/Makemodule.am
  7. 4
      misc-utils/meson.build
  8. 347
      misc-utils/pipesz.c

1
.gitignore vendored

@ -148,6 +148,7 @@ GSYMS
/nsenter
/partx
/pg
/pipesz
/pivot_root
/prlimit
/raw

8
configure.ac

@ -1785,6 +1785,14 @@ AC_ARG_ENABLE([whereis],
UL_BUILD_INIT([whereis])
AM_CONDITIONAL([BUILD_WHEREIS], [test "x$build_whereis" = xyes])
AC_ARG_ENABLE([pipesz],
AS_HELP_STRING([--disable-pipesz], [do not build pipesz]),
[], [UL_DEFAULT_ENABLE([pipesz])]
)
UL_BUILD_INIT([pipesz])
UL_REQUIRES_LINUX([pipesz])
AM_CONDITIONAL([BUILD_PIPESZ], [test "x$build_pipesz" = xyes])
UL_BUILD_INIT([getopt], [yes])
AM_CONDITIONAL([BUILD_GETOPT], [test "x$build_getopt" = xyes])

4
include/pathnames.h

@ -198,6 +198,10 @@
#define _PATH_PROC_UCLAMP_MIN _PATH_PROC_KERNEL "/sched_util_clamp_min"
#define _PATH_PROC_UCLAMP_MAX _PATH_PROC_KERNEL "/sched_util_clamp_max"
/* sysctl fs paths */
#define _PATH_PROC_SYS_FS "/proc/sys/fs"
#define _PATH_PROC_PIPE_MAX_SIZE _PATH_PROC_SYS_FS "/pipe-max-size"
/* irqtop paths */
#define _PATH_PROC_INTERRUPTS "/proc/interrupts"
#define _PATH_PROC_SOFTIRQS "/proc/softirqs"

12
meson.build

@ -2683,6 +2683,18 @@ if not is_disabler(exe)
bashcompletions += ['hardlink']
endif
opt = not get_option('build-pipesz').disabled()
exe = executable(
'pipesz',
pipesz_sources,
include_directories : includes,
link_with : [lib_common],
install_dir : usrbin_exec_dir,
install : true)
if opt and not is_disabler(exe)
exes += exe
endif
exe = executable(
'test_cal',
cal_sources,

2
meson_options.txt

@ -150,6 +150,8 @@ option('build-more', type : 'feature',
description : 'build more')
option('build-pg', type : 'feature',
description : 'build pg')
option('build-pipesz', type : 'feature',
description : 'build pipesz')
option('build-setterm', type : 'feature',
description : 'build setterm')
option('build-schedutils', type : 'feature',

7
misc-utils/Makemodule.am

@ -267,3 +267,10 @@ lsfd_SOURCES = \
lsfd_LDADD = $(LDADD) libsmartcols.la libcommon.la
lsfd_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
endif
if BUILD_PIPESZ
bin_PROGRAMS += pipesz
pipesz_SOURCES = misc-utils/pipesz.c
pipesz_LDADD = $(LDADD) libcommon.la
pipesz_CFLAGS = $(AM_CFLAGS)
endif

4
misc-utils/meson.build

@ -140,3 +140,7 @@ hardlink_sources = files(
cal_sources = files(
'cal.c',
)
pipesz_sources = files(
'pipesz.c',
)

347
misc-utils/pipesz.c

@ -0,0 +1,347 @@
/*
* 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;
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]);
}
Loading…
Cancel
Save