(root)/
util-linux-2.39/
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  	printf(_(" %s [options] [--set <size>] [--] [command]\n"), program_invocation_short_name);
      66  	printf(_(" %s [options] --get\n"), program_invocation_short_name);
      67  
      68  	fputs(USAGE_SEPARATOR, stdout);
      69  	/* TRANSLATORS: 'command' refers to a program argument */
      70  	puts(_("Set or examine pipe buffer sizes and optionally execute command."));
      71  
      72  	fputs(USAGE_OPTIONS, stdout);
      73  	puts(_(" -g, --get          examine pipe buffers"));
      74  	/* TRANSLATORS: '%s' refers to a system file */
      75  	printf(
      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  	puts(_(" -f, --file <path>  act on a file"));
      82  	puts(_(" -n, --fd <num>     act on a file descriptor"));
      83  	puts(_(" -i, --stdin        act on standard input"));
      84  	puts(_(" -o, --stdout       act on standard output"));
      85  	puts(_(" -e, --stderr       act on standard error"));
      86  
      87  	fputs(USAGE_SEPARATOR, stdout);
      88  	puts(_(" -c, --check        do not continue after an error"));
      89  	puts(_(" -q, --quiet        do not warn of non-fatal errors"));
      90  	puts(_(" -v, --verbose      provide detailed output"));
      91  
      92  	fputs(USAGE_SEPARATOR, stdout);
      93  	printf(USAGE_HELP_OPTIONS(20));
      94  
      95  	printf(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  			fd = 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  }