Decoded: stdbuf (coreutils)

[Back to Project Main Page]

Note: This page explores the design of command-line utilities. It is not a user guide.
[GNU Manual] [No POSIX requirement] [Linux man] [FreeBSD man]

Logical flow of stdbuf command (coreutils)

Summary

stdbuf - run a command with modified I/O stream buffering

[Source] [Code Walkthrough]

Lines of code: 395
Principal syscall: execvp()
Support syscalls: stat()
Options: 8 (3 short, 5 long)

This is a relatively recent utility
Added to Shellutils in June 2009 [First version]
Number of revisions: 44 [Code Evolution]

The stdbuf utility alters the buffering mode of the standard streams for the given command to either unbuffered, line buffered, or fully buffered. By default, we assume STDERR is unbuffered, while STDIN and STDOUT are fully buffered in most cases (exception: line buffered for true terminals). The mechanism to change buffering mode relies on LD_PRELOAD to load the libstdbuf shared object which applies changes at load time using the setvbuf() syscall

Helpers:
  • optc_to_fileno() - Converts a character to a stream reference
  • parse_size() - Parses buffer size by converting the string to an integer.
  • set_LD_PRELOAD() - Adds libstdbuf to LD_PRELOAD
  • set_libstdbuf_options() - Add the desired streams to the environment
  • set_program_path() - Sets the program_path global from $PATH to include the program to execute
External non-standard helpers:
  • die() - Exit with mandatory non-zero error and message to stderr
  • error() - Outputs error message to standard error with possible process termination

Setup

stdbuf keeps two important elements at global scope. The first is *program_path, which holds the computed path of the program to run. The second global is a struct, stdbuf to hold the details of the execution.

main() defines a local variable, c to hold the first letter of the next option to parse

Parsing begins with the short options passed as a string literal:
"+i:o:e:"


Parsing

Parsing stdbuf uses Getopt to pull user input to change buffering across the three input streams. A fully buffered steam includes parsing a buffer size:

Parsing failures

Parsing could fail in several ways:

  • The user requests line buffering of STDIN
  • User provides an invalid mode
  • No command to execute
  • No change to stream buffering modes

Execution

stdbuf relies on a load-time mechanism to adjust streams before executing the target command. It takes advantage of the GCC constructor attrbiute within libstdbuf.c. This compiled library is loaded and executed before stdbufmain() by adding it to LD_PRELOAD.

The overall utility logic is brief:

  • Update the stream options in the global struct stdbuf array
  • Set the program path
  • Update the LD_PRELOAD with the new library
  • Execute target commend with updated constructor modifying streams

Note that since a successful execvp() never returns, any execution within stdbuf implicilty means something failed. The subsequent command should return successful but the utility itself has no 'successful' return.

The stdbuf utility may fail in several ways. An EXIT_CANCELED status indicates a failure of stdbuf, while EXIT_ENOENT or EXIT_CANNOT_INVOKE points to a problem with the target command.

Failure cases:

  • Unable to set environment options
  • Unable to set LD_PRELOAD
  • execvp() on target command fails

All failures at this stage output an error message to STDERR and return without displaying usage help


[Back to Project Main Page]