PStreams Frequently Asked Questions (FAQ)

Some of these questions haven't even been asked once, and if I write this maybe they never will be. If you have questions not answered here please send them to the author or one of the PStreams mailing lists.

Why use PStreams instead of popen()?

Aside from all the usual reasons to prefer IOStreams to stdio, the PStreams classes give you access to any combination of a process' stdin, stdout and stderr, as well as interfaces to wait(2), kill(2) and other system calls. Most implementations of popen(3) only support unidirectional I/O and do not give you as much control of the process.

What can I use PStreams for ?

Simple text-based IPC between two processes. There are more flexible and powerful forms of IPC, but for many applications sending and receiving text on the standard streams is sufficient. PStreams makes this almost as easy as file IO (as it should be in UNIX!)

The original aim was to re-use functionality of existing programs rather than re-implement them, e.g. read the contents of a directory using ls(1), or send an email using mail(1), or perform arbitrary precision arithmetic using dc(1). The overhead of forking and communicating with a new process might not matter if a well-known and well-tested program already exists with the required functionality. This fits well in the UNIX tradition of combining simple tools into more complex systems using pipes and filters.

You can also use PStreams within a GUI application to run a child process and capture it's output for display in the parent process.

Why does seeking on a pstream always fail ?

PStreams uses pipes (see pipe(2)) to communicate with the controlled process and it is not possible to reposition a stream associated with a pipe. The pstreambuf class supports putting up to pstreams::pbackfail characters back into the stream buffer, but this does not return the characters to the pipe or have any effect on the controlled process.

How do I tell if the child process started OK?

Use open(string, vector<string>, pmode) and call is_open().

How do I get exit status of child process?

Call pstreambuf::status() after the process has exited.

  ipstream s("hostname");
  s.close();
  if (s.rdbuf()->exited())
     std::cout << "Exit status was " << r.rdbuf()->status();

Why does my program hang in the pstreambuf destructor ?

The destructor waits for the child process to exit. Before the pstreambuf destructor runs you should ensure the process exits (e.g. by sending EOF if it reads from stdin) or send a signal using pstreambuf::kill().

TODO add a "kill_in_dtor" member to pstreambuf so the child gets killed in the dtor, so throwing an exception doesn't hang in dtor if the child is still running.

How do I send EOF to the child process?

If the child process needs to read all data before it produces output and/or exits then you must close the child process' stdin so that it receives the end-of-file indicator. This is done by calling pstreambuf::peof(), or inserting the peof manipulator into the stream.

What if the child process does not print any output until all the input has been read ?

See How do I send EOF to the child process?

Why does my program block when trying to read from the child ?

If the child process has produced output but reading from the stream blocks indefinitely then it is probably caused by the buffering done by pstreambuf. By default pstreambuf will try to fill its internal buffer when reading from the child process; if the number of characters available in the pipe is less than the buffer size then the read will block until more input is available. (The buffer size is given by the constant pstreams::bufsz.)

The std::istream interface is not really designed for non-blocking I/O but we do have the std::streambuf::in_avail() function, which tells you how many characters can be read from the stream without blocking. PStreams uses in_avail() to avoid the default blocking behaviour when reading. If in_avail() is called on a pstreambuf when its internal character buffer is empty, a non-blocking read will be used to fill the buffer. So if in_avail() returns a positive number then that many characters are already in the buffer and can be read immediately without blocking. Rather than of calling in_avail() directly, you can use std::istream::readsome(char_type* s, streamsize n) which will read min(rdbuf()->in_avail(), n) characters and store them at s.

How do I set a time-out when reading from the child process ?

PStreams does not provide any way of setting a time out but it's fairly easy to implement using signals. A signal can be scheduled using alarm(2) or setitimer(2) and a handler can set a flag to indicate the signal was raised. If the flag is set when the read returns it indicates that the signal was raised before the read completed, so you should take appropriate action, such as killing the child process.

If using alarm signals in this way, be sure to reset the alarm, the flag and the signal handler after the read returns.

How do I read errors from the child process ?

If the pstream was opened with pstreams::pstderr in the pmode flags then the stream buffer will read from a pipe attached to the child process' standard error stream (stderr). The data from the process' standard output and standard error are kept separate and the stream buffer can only read one at a time. You can change which stream is currently active using the ipstream::out() and ipstream::err() member functions.

If you have read all the available data from the child's stdout and reached EOF then eofbit will be set in the stream state, so you will need to clear it by calling clear() on the pstream object before you can read from the stderr (and vice versa if you reach EOF on the stderr stream and then want to read stdout).

A simpler alternative when launching a process with a string is to create the child process with "./command 2>&1" which will cause its standard error to be combined with the standard output stream.

What command should be passed to open() ?

The pstreambuf::open() function and the constructors for all the pstreams types are overloaded to allow them to be called in two ways:

Creating a process from a vector of arguments

These overloads use execvp(3) so emulate the behaviour of the shell in searching for a file to execute. Each argument is passed to the new process individually without any word splitting or expansion done by the shell.

In C++11 you can pass an initializer list to these overloads, e.g. redi::pstream ip({ "grep", "pattern", "file" });.

Creating a process from a string

These overloads take a std::string containing one or more shell commands which will be run as /bin/sh -c command by calling execl(3).

The shell will process and run the commands in the string, performing the usual steps such as word splitting and expansions. This allows wildcards and redirections to be used but is vulnerable to shell injection attacks if used with arbitrary user input, so should be used with care.

Possible future extension: allow user to specify something other than /bin/sh

Possible future extension: allow user to choose to use execv instead of execvp, or execlp instead of execl.

Which compilers can I use PStreams with ?

You should be able to use PStreams with any fairly standard-conforming compiler on a POSIX system (i.e. UNIX-like). The most important thing is that the compiler supports standard IOStreams.

What signals/exceptions can PStreams raise ?

SIGCHLD when child exits etc. SIGPIPE if write to closed pipe (after child exits). Usual IOStreams exceptions, none from PStreams itself. Signals should be handled by the caller. TODO - make exception-safe, all allocations use ScopeGuard.

Are PStreams exception-safe ?

PStreams classes are designed to provide the basic exception-safety guarantee. If you find any leaks or crashes they are bugs that will be fixed if you report them.

However, If an exception is thrown and causes an active pstreambuf to be destroyed the process could hang indefinitely if the child process does not exit. TODO - pguard, calls kill in dtor.

Are PStreams thread-safe ?

Pstreams follows the same rules as the C++11 standard library: accessing an object from multiple threads is only safe if all concurrent accesses use const members only. If any thread uses a non-const member function then you are responsible for performing the necessary synchronisation to ensure that no other thread accesses the object at the same time.

Can I use PStreams on Windows ?

Only by using Cygwin, PStreams only works on POSIX systems.

If you only want a console application you can use the old popen-based branch (release 0.17), which uses the Win32 functions _popen() and _pclose(), but these will not work in a GUI program (and might blow up your PC, if Windows doesn't do it for you.) This version doesn't have most of the library's features and is no longer maintained or tested.

You could also try libexecstream instead, which is similar to PStreams but cross-platform.

Is the API stable ?

Yes. The current code will eventually be released as version 1.0 without many more changes. Version 2.0 will have a new process class that pstreambuf will use, so there will be a number of changes due to that, but that may never actually get written! Since all PStreams classes are templates it is not possible to hide changes to the implementation in a library, so even if the public interface remains stable you might need to recompile all your code that uses PStreams if you use a newer version of pstream.h.

I might also consider the following changes before version 1.0.0:
is_open() could be renamed to be clear it means "process was executed".
exited() could be renamed to be clear it means "executed process has stopped running".