RUNCAP(2)

NAME
runcap - run external process and capture its stdout and stderr

SYNOPSIS
#include <runcap.h>

int runcap(struct runcap *rc, int flags);
void runcap_free(struct runcap *rc);

int runcap_getc(struct runcap *rc, int stream, char *cp);
ssize_t runcap_getline(struct runcap *rc, int stream,
                       char **pstr, size_t *psize);

off_t runcap_tell(struct runcap *rc, int stream);
off_t runcap_seek(struct runcap *rc, int stream, off_t off, int whence);
int runcap_rewind(struct runcap *rc, int stream);

DESCRIPTION
The function runcap runs an external command, and waits for its termination, capturing its standard output and standard error streams, and optionally supplying data at its standard input.

The rc argument points to struct runcap, which serves two purposes. On input, it supplies the necessary data for running the command. The caller is responsible for pointing the rc_argv member to the vector of command line arguments. This member must always be initialized. If any of the remaining members are initialized, the caller must inform the runcap function about it by setting appropriate bit in the flags argument.

The function returns 0 on success, and -1 on error.

Upon return from runcap, the remaining members of the structure will be filled with the information about the termination status of the program and the captured content of its standard output and standard error streams.

Whatever the return status, when no longer used the structure must be freed using runcap_free.

  The struct runcap argument
The struct runcap is defined as follows:

struct runcap
{
    char *rc_program;    /* [IN] (Path)name of the program to run */
    char **rc_argv;      /* [IN] Argument vector */
    unsigned rc_timeout; /* [IN] Execution timeout */
    struct stream_capture rc_cap[3];
    pid_t rc_pid;        /* [OUT] PID of the process */
    int rc_status;       /* [OUT] Termination status */
    int rc_errno;        /* [OUT] System error code */
};

Members that may be initialized on input are marked with [IN], those that are set upon return from runcap() are marked with [OUT].

The caller is responsible for initializing the rc_argv member with a pointer to the command line arguments array. Other input fields are:

rc_program
    Actual pathname of the program to be executed. If initialized, the RCF_PROGRAM bit must be set in flags. Otherwise, the value of rc_argv[0] is assumed.

rc_timeout
    Time to wait for the program termination, in seconds. If initialized, the RCF_TIMEOUT bit must be set in flags. If not set, runcap will wait indefinitely.

The three streams associated with the running command are described by the rc_cap array. Its elements correspond to the standard input, output and error streams. Upon successful return, the captured data from stdout and stderr can be retrieved using the runcap_getc, and runcap_getline functions (see below). For convenience, the following constants are defined in runcap.h to refer to the corresponding streams: RUNCAP_STDIN, RUNCAP_STDOUT, RUNCAP_STDERR.

The stream_capture structure contains the following members accessible to the caller:

struct stream_capture
{
    size_t sc_size;      /* [IN] size of the buffer */
    void (*sc_linemon)(const char *, size_t, void *);
                         /* [IN] Line monitor function */
    void  *sc_monarg;    /* [IN] Value for its 3rd parameter */
    off_t  sc_leng;      /* [OUT] total length of captured data */
    size_t sc_nlines;    /* [OUT] number of captured lines */
    int    sc_storfd;    /* [IN] storage file descriptor */
    /* The following two are available only in rc_cap[RUNCAP_STDIN]
       For details. see the subsection Supplying standard input.
     */
    int  sc_fd;          /* Input file descriptor */
    char *sc_base;       /* Input data */
};

The caller may initialize the following members in rc_cap[RUNCAP_STDOUT] and rc_cap[RUNCAP_STDERR]:

sc_size
    Size of the buffer (size_t). If set, the RCF_STDOUT_SIZE (for RUNCAP_STDOUT) or RCF_STDERR_SIZE (for RUNCAP_STDERR) bit must be set in flags. If the amount of input data exceeds the buffer size, the data are saved in the disk file, therefore setting a larger buffer can improve performance. Setting sc_size to 0 disables capturing. Same effect is achieved by setting the RCF_STDOUT_NOCAP or RCF_STDERR_NOCAP flag. Default buffer size is 4096 bytes.

sc_linemon
    A pointer to the line monitor function. If set, the RCF_STDOUT_LINEMON (for RUNCAP_STDOUT), or RCF_STDERR_LINEMON (for RUNCAP_STDERR) bit must be set in flags.

    Line monitor function allows the caller to monitor the arrival of the new data in the corresponding stream. It is declared as follows:

    void linemon(const char *line, size_t size, void *data)

    where line is the line that has been read, size is its length, and data is an opaque pointer to application-specific data, supplied in the sc_monarg member.

    The line monitor function is invoked to notify the caller about arrival of new data on the stream. There are 4 cases:

    .   A newline newline character is encountered in the stream.

        The line argument points to the beginning of the captured line. The size argument contains number of characters in the line, including the newline character.

    .   The stream buffer is full (and therefore is about to be flushed into the storage file) and contains unfinished line.

        The line argument points to the beginning of the captured line fragment. The size argument contains number of characters in the fragment.

    .   No more characters are available in the stream and the stream buffer contains unreported characters.

        Argument assignment is the same as in previous case.

    .   The stream reached EOF.

        The size argument is 0. The line argument is not meaningful, although it does point to a valid memory location.

    Consequently, if the sc_linemon function needs to process full lines. it has to keep the state of processing (e.g. in the data argument), and concatenate the line parameters until the following condition is met:

          size == 0 || line[size-1] == '\n'

sc_monarg
    The value of the data parameter for the sc_linemon function.

sc_storfd
    File descriptor of the storage file for that stream. If initialized, the RCF_STDOUT_STORFD or RCF_STDERR_STORFD bit must be set in flags. All data obtained by that stream will be written to this descriptor, in addition to the normal processing.

  Supplying standard input
The rc_cap[RUNCAP_STDIN] field can be used to supply standard input for the command. The input can be supplied either as a character string, or as a file descriptor. To use the first method, initialize rc_cap[RUNCAP_STDIN].sc_base with the pointer to the string, rc_size with its length, and set sc_fd to -1.

To use the second method, set rc_cap[RUNCAP_STDIN].sc_fd to the file descriptor opened for reading, and set rc_cap[RUNCAP_STDIN].sc_base to NULL.

Whichever approach is used, set the RCF_STDIN bit in flags to inform runcap() about the fact.

  Output
Upon return, the following fields are initialized:

rc_status
    Termination status, as returned by wait(2).

rc_errno
    Value of errno, if terminated on error.

The following fields are defined upon successful return:

rc_cap[RUNCAP_STDOUT].sc_leng
    Total length of captured stdout.

rc_cap[RUNCAP_STDOUT].sc_nlines
    Number of lines in the captured stdout.

rc_cap[RUNCAP_STDERR].sc_leng
    Total length of captured stderr.

rc_cap[RUNCAP_STDERR].sc_nlines
    Number of lines in the captured stderr.

The last captured line can be partial, i.e. not terminated with a newline character. It will be counted in the stream's sc_nlines anyways.

Captured data can be retrieved using the runcap_getc, and runcap_getline functions, described below.

  Examining output
Upon return from runcap the following functions can be used to retrieve captured data from the struct runcap object pointed to by its rc argument. The stream to retrieve data from is identified by the stream argument, whose valid values are RUNCAP_STDOUT (or 1) or RUNCAP_STDERR (or 2).

The function runcap_getc reads the next character from the captured stream and returns it as an unsigned char cast to an int. It returns 0 on end of stream, and -1 on error. In the latter case, the errno variable contains the error code, as usual.

The function runcap_getline reads all characters from the current position in the stream up to and including the next newline character (ASCII 10). It will allocate the buffer for the characters as necessary and will store the address of the buffer into *pstr, and its allocated size in *psize. The buffer is null-terminated and includes the newline character, if one was found.

If *pstr is NULL, the function will allocate a buffer of sufficient size for storing the line.

Otherwise, *pstr should contain a pointer to a buffer *psize bytes in size, allocated using malloc(3). If the buffer is not large enough to hold the characters, runcap_getline will resize it using realloc(3), updating *pstr and *psize as necessary.

On success, runcap_getline returns the number of characters (including the newline) stored in the buffer, or 0 if end of stream is reached. On error, it returns -1 and sets errno.

The function runcap_tell returns offset in bytes to the current position in the requested stream.

The function runcap_seek repositions the offset of the requested stream to the argument offset according to the directive whence as follows:

SEEK_SET
    The offset is set to offset bytes.

SEEK_CUR
    The offset is set to its current location plus offset bytes.

SEEK_END
    The offset is set to the size of the stream (sc_leng) plus offset bytes.

The function returns 0 on success. On error, it returns -1 and sets errno.

The function runcap_rewind repositions the current offset of stream to 0.

The following pairs of calls are equivalent:

runcap_tell(rc, stream) <=> runcap_seek(rc, stream, 0, SEEK_CUR)

and

runcap_rewind(rc, stream) <=> runcap_seek(rc, stream, 0, SEEK_SET)

  Redirecting output
In addition to the normal processing, the output sent to a particular stream can be redirected to a file. To do so, initialize the sc_storfd member of the output stream to the file descriptor and set the RCF_STDOUT_STORFD or RCF_STDERR_STORFD flag (depending on the stream). For example, the following code redirects the standard output of the program to the file "outfile":

int fd = open("outfile", O_CREAT|O_RDWR|O_TRUNC, 0600);
assert(fd != -1);
rc->rc_cap[RUNCAP_STDOUT].sc_storfd = fd;
runcap(&rc, RCF_STDOUT_STORFD);

  Disabling capturing
Two ways are provided to disable capturing of a particular stream. First, you can set the sc_size member of the corresponding rc_cap element to 0 and indicate that by setting the RCF_STDOUT_SIZE (for stdout) or RCF_STDERR_SIZE (for stderr) flag. This was the method used in first releases of runcap.

Another way is to use the RCF_STDOUT_NOCAP and/or RCF_STDERR_NOCAP flag in the call to runcap.

Both methods are equivalent.

When capturing of a particular stream is disabled, the corresponding file descriptor of the program being invoked is inherited from the calling program. This means, e.g., that if you disable stderr capturing, error diagnostics from the programs that runcap runs will appear at the stderr of your program. Upon return from runcap both sc_leng and sc_nlines fields of the corresponding rc_cap structure will be zero.

This is changed if line monitor is installed on the stream with disabled capturing. In that case, the output from the program will be intercepted, passed to the line monitor function and discarded. Upon return, both sc_leng and sc_nlines will reflect the actual length in bytes and number of lines in the processed material. sc_nlines field.

RETURN VALUE
Upon successful completion, runcap() returns 0. The rc.rc_status value should be inspected to see if the program terminated successfully. On error, it sets the rc.rc_errno and returns -1.

The runcap_getc() returns the retrieved character on success, 0 if end of stream is hit, and -1 on error.

The function runcap_getline() returns the number of retrieved characters (including the newline) on success, 0 on end of stream, and -1 on error.

runcap_tell() and runcap_seek() return the current offset (a non-negative value) on success, and -1 on error.

runcap_rewind() returns 0 on success and -1 on error.

When returning an error (-1), all functions set the global errno variable to the code describing the error.

EXAMPLE
The function below runs the tar(1) command in verbose mode and returns its output as an array of strings.

char **
archive(void)
{
    struct runcap rc;
    int res;
    char *av[] = { "tar", "cfv", "src.tar", "src", NULL };
    char **ret = NULL;

    rc.rc_program = "/bin/tar";
    rc.rc_argv = av;
    res = runcap(&rc, RCF_PROGRAM);
    if (res == -1) {
        perror("runcap");
        abort();
    }
    if (WIFEXITED(rc.rc_status) && WEXITSTATUS(rc.rc_status) == 0) {
        char **ret;
        size_t nlines = rc.rc_cap[RUNCAP_STDOUT].sc_nlines;
        ret = malloc(nlines + 1);
        for (i = 0; i < nlines; i++) {
            char *p = NULL;
            size_t sz = 0;
            ssize_t n;
            n = runcap_getline(rc, stream, p, &sz);
            if (n == -1) {
                perror("runcap_getline");
                abort();
            }
            ret[i] = realloc(p, n + 1);
        }
        ret[i] = NULL;
    }
    runcap_free(&rc);
    return ret;
}

AUTHORS
Sergey Poznyakoff

COPYRIGHT
Copyright © 2017--2020 Sergey Poznyakoff
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.