authorSergey Poznyakoff <>2017-07-19 12:23:20 (GMT)
committer Sergey Poznyakoff <>2017-07-19 14:07:26 (GMT)
commit99d911104d7c03608398d47c74f1ce3c39bff211 (patch) (side-by-side diff)
parent651babfefa21536112cbcd799932b3595aeb2da4 (diff)
Include the manpage; improve make distcheck
Diffstat (more/less context) (ignore whitespace changes)
8 files changed, 400 insertions, 12 deletions
diff --git a/.gitignore b/.gitignore
index f6aee58..a712fcb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ core
diff --git a/Makefile b/Makefile
index 36cec3e..1f0914d 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,8 @@ PREFIX = /usr
MANDIR = $(PREFIX)/share/man
-MAN2DIR = $(MANDIR)/man2
+MAN3DIR = $(MANDIR)/man3
+MAN3FILES = runcap.3
# The install program. Use cp(1) if not available.
INSTALL = install
@@ -61,15 +62,15 @@ install-headers: runcap.h
-# $(INSTALL) runcap.2 $(DESTDIR)$(MAN2DIR)
.PHONY: check distdir
check: all
$(MAKE) -C t check
test -d $(DISTDIR) || mkdir $(DISTDIR)
@@ -81,10 +82,13 @@ dist: distdir
rm -rf $(DISTDIR)
distcheck: dist
- @mkdir _build; \
+ @mkdir _build _inst; \
cd _build; \
tar xzf ../$(DISTDIR).tar.gz; \
- if $(MAKE) -C $(DISTDIR) check; then \
+ if $(MAKE) -C $(DISTDIR) check \
+ && $(MAKE) -C $(DISTDIR) install DESTDIR=`cd ../_inst; pwd`; then \
+ cd ..; \
+ rm -rf _build _inst; \
echo "$(DISTDIR).tar.gz is ready for distribution";\
else \
echo "Please, inspect " `pwd`;\
diff --git a/getc.c b/getc.c
index ed6713d..eb7607a 100644
--- a/getc.c
+++ b/getc.c
@@ -30,7 +30,7 @@ runcap_getc(struct runcap *rc, int sd, char *cp)
return -1;
- cap = runcap_stream_capture(rc, sd);
+ cap = runcap_get_capture(rc, sd);
if (!cap)
return -1;
diff --git a/runcap.3 b/runcap.3
new file mode 100644
index 0000000..aa51e4e
--- a/dev/null
+++ b/runcap.3
@@ -0,0 +1,382 @@
+.\" This file is part of runcap -*- nroff -*-
+.\" Copyright (C) 2016 Sergey Poznyakoff
+.\" Runcap 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; either version 3, or (at your option)
+.\" any later version.
+.\" Runcap is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" GNU General Public License for more details.
+.\" You should have received a copy of the GNU General Public License
+.\" along with runcap. If not, see <>.
+.TH RUNCAP 2 "July 19, 2017" "RUNCAP" "User Commands"
+runcap \- run external process and capture its stdout and stderr
+.B #include <runcap.h>
+.BI "int runcap(struct runcap *" rc ", int " flags );
+.BI "void runcap_free(struct runcap *" rc );
+.BI "int runcap_getc(struct runcap *" rc ", int " stream ", char *" cp );
+.BI "ssize_t runcap_getline(struct runcap *" rc ", int " stream ,
+.BI " char **" pstr ", size_t *" psize );
+.BI "off_t runcap_tell(struct runcap *" rc ", int " stream );
+.BI "int runcap_seek(struct runcap *" rc ", int " stream ", off_t " off ", int " whence);
+.BI "int runcap_rewind(struct runcap *" rc ", int " stream );
+The function
+.B 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 \fIrc\fR argument points to \fBstruct runcap\fR, which serves two
+purposes. On input, it supplies the necessary data for running the
+command. The caller is responsible for pointing the
+.B 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
+.B runcap
+function about it by setting appropriate bit in the \fIflags\fR
+The function returns 0 on success, and -1 on error.
+Upon return from
+.BR 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
+.BR runcap_free .
+.SS The struct runcap argument
+The \fBstruct runcap\fR is defined as follows:
+.PP +4n
+struct runcap
+ char *rc_program; /* [\fIIN\fR] (Path)name of the program to run */
+ char **rc_argv; /* [\fIIN\fR] Argument vector */
+ unsigned rc_timeout; /* [\fIIN\fR] Execution timeout */
+ struct stream_capture rc_cap[3];
+ pid_t rc_pid; /* [\fIOUT\fR] PID of the process */
+ int rc_status; /* [\fIOUT\fR] Termination status */
+ int rc_errno; /* [\fIOUT\fR] System error code */
+The members that may be initialized on input are marked with
+[\fIN\fR], those that are set upon return from \fBruncap()\fR are
+marked with [\fOUT\fR].
+The caller is responsible for initializing the \fBrc_argv\fR member
+with a pointer to the command line arguments array. The other
+input fields are:
+.B rc_program
+Actual pathname of the program to be executed. If initialized, the
+\fBRCF_PROGRAM\fR bit must be set in \fIflags\fR. Otherwise, the
+value of \fBrc_argv[0]\fR is assumed.
+.B rc_timeout
+Time to wait for the program termination, in seconds. If initialized,
+the \fBRCF_TIMEOUT\fR bit must be set in \fIflags\fR. If not set,
+.B runcap
+will wait indefinitely.
+The three streams associated with the running command are described by
+.B rc_cap
+array. Its elements correspond to the standard input, output and
+error streams, correspondingly. Upon successful return, the captured
+data from stdin and stdout can be retrieved using the \fBruncap_getc\fR, and
+\fBruncap_getline\fR functions (see below). For convenience, the
+following constants are defined in \fBruncap.h\fR to refer to the
+corresponding streams:
+The following fields are warranted to be present in each \fBstruct
+.PP +4n
+struct stream_capture
+ size_t sc_size; /* [\fIIN\fR] size of the buffer */
+ void (*sc_linemon)(const char *, size_t, void *);
+ /* [\fIIN\fR] Line monitor function */
+ void *sc_monarg; /* [\fIIN\fR] Value for its 3rd parameter */
+ off_t sc_leng; /* [\fIOUT\fR] total length of captured data */
+ size_t sc_nlines; /* [\fIOUT\fR] number of captured lines */
+ /* The following two are available only in \fBrc_cap[RUNCAP_STDIN]\fR,
+ see the subsection \fBSupplying standard input\fR below, for
+ details). */
+ size_t sc_fd; /* Input file descriptor */
+ char *sc_base; /* Input data */
+The caller may initialize the following members in
+\fBrc_cap[RUNCAP_STDOUT]\fR and \fBrc_cap[RUNCAP_STDERR]\fR:
+.B sc_size
+Size of the buffer (\fBsize_t\fR). If set, the \fBRCF_STDOUT_SIZE\fR
+\fBRUNCAP_STDERR\fR) bit must be set in \fIflags\fR. The default
+buffer size is 4096 bytes. 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 \fBsc_size\fR to 0
+disables capturing.
+.B sc_linemon
+A pointer to the line monitor function. If set, the
+\fBRCF_STDERR_LINEMON\fR (for \fBRUNCAP_STDERR\fR) bit must be set in
+The line monitor function allows the caller to monitor the arrival of
+each input line in the corresponding stream. Its signature is:
+.BI "void linemon(const char *" line ", size_t " size ", void *" data )
+where \fIline\fR is the line that has been read, \fIsize\fR is its
+length, and \fIdata\fR is an opaque pointer to application-specific
+data, supplied in the \fBsc_monarg\fR member.
+.B sc_monarg
+The value of the \fIdata\fR parameter for the \fBsc_linemon\fR
+function. It can be initialized only if \fBsc_linemon\fR is
+initialized as well.
+.SS Supplying standard input
+.B 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
+.B rc_cap[RUNCAP_STDIN].sc_base
+with the pointer to the string,
+.B rc_size
+with its length, and set
+.B sc_fd
+to -1.
+To use the second method, set
+.B rc_cap[RUNCAP_STDIN].sc_fd
+to the file descriptor opened for reading, and set
+.B rc_cap[RUNCAP_STDIN].sc_base
+Whichever approach is used, set the \fBRCF_STDIN\fR bit in \fIflags\fR
+to inform
+.B runcap()
+about the fact.
+.SS Output
+Upon return, the following fields are initialized:
+.B rc_status
+Termination status, as returned by
+.BR wait (2).
+.B rc_errno
+Value of errno, if terminated on error.
+Upon successful return, the following fields are initialized:
+.B rc_cap[RUNCAP_STDOUT].sc_leng
+Total length of captured stdout.
+.B rc_cap[RUNCAP_STDOUT].sc_nlines
+Number of lines in the captured stdout.
+.B rc_cap[RUNCAP_STDERR].sc_leng
+Total length of captured stderr.
+.B rc_cap[RUNCAP_STDERR].sc_nlines
+Number of lines in the captured stderr.
+The actual data can be retrieved using the \fBruncap_getc\fR, and
+\fBruncap_getline\fR functions, described below.
+.SH Examining output
+Upon return from \fBruncap\fR the following functions can be used to
+retrieve the captured data from the \fBstruct runcap\fR object pointed
+to by its \fIrc\fR argument. The stream to retrieve the data from is
+identified by the \fIstream\fR argument, whose valid values are
+\fBRUNCAP_STDOUT\fR (or \fB1\fR) or \fBRUNCAP_STDERR\fR (or \fB2\fR).
+The function \fBruncap_getc\fR 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 \fBerrno\fR variable contains the error code, as usual.
+The function \fBruncap_getline\fR 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
+\fB*pstr\fR, and its allocated size in \fB*psize\fR. The buffer is
+null-terminated and includes the newline character, if one was found.
+If \fB*pstr\fR is \fBNULL\fR, the function will allocate a buffer of
+sufficient size for storing the line.
+Otherwise, \fB*pstr\fR should contain a pointer to a buffer
+\fB*psize\fR bytes in size, allocated using
+.BR malloc (3).
+If the buffer is not large enough to hold the characters,
+.B runcap_getline
+will resize it using
+.BR realloc (3),
+updating \fB*pstr\fR and \fB*psize\fR as necessary.
+On success,
+.B 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 \fBerrno\fR.
+The function \fBruncap_tell\fR returns offset in bytes to the current
+position in the requested stream.
+The function \fBruncap_seek\fR repositions the offset of the requested
+\fIstream\fR to the argument \fIoffset\fR according to the directive
+\fIwhence\fR as follows:
+The offset is set to \fIoffset\fR bytes.
+The offset is set to its current location plus \fIoffset\fR bytes.
+The offset is set to the size of the stream (\fBsc_leng\fR) plus
+\fIoffset\fR bytes.
+The function returns 0 on success. On error, it returns -1 and sets
+The function \fBruncap_rewind\fR repositions the current offset of
+\fIstream\fR to 0.
+The following pairs of calls are equivalent:
+.PP +4n
+runcap_tell(rc, stream) <=> runcap_seek(rc, stream, 0, SEEK_CUR)
+.sp +4n
+runcap_rewind(rc, stream) <=> runcap_seek(rc, stream, 0, SEEK_SET)
+Upon successful completion,
+.B runcap()
+returns 0. The \fBrc.rc_status\fR value should be inspected to see if
+the program terminated successfully. On error, it sets the
+\fBrc.rc_errno\fR and returns -1.
+.B runcap_getc()
+returns the retrieved character on success, 0 if end of stream is hit,
+and -1 on error.
+The function
+.B runcap_getline()
+returns the number of retrieved characters (including the newline) on
+success, 0 on end of stream, and -1 on error.
+.B runcap_tell()
+returns the current offset (a non-negative value) on success, and -1
+on error.
+.B runcap_seek()
+.B runcap_rewind()
+return 0 on success and -1 on error.
+When returning an error (-1), all functions set the global \fBerrno\fR
+variable to the code describing the error.
+char **
+ 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;
+Sergey Poznyakoff
+Copyright \(co 2017 Sergey Poznyakoff
+License GPLv3+: GNU GPL version 3 or later <>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+.\" Local variables:
+.\" eval: (add-hook 'write-file-hooks 'time-stamp)
+.\" time-stamp-start: ".TH [A-Z_][A-Z0-9_.\\-]* [0-9] \""
+.\" time-stamp-format: "%:B %:d, %:y"
+.\" time-stamp-end: "\""
+.\" time-stamp-line-limit: 20
+.\" end:
diff --git a/runcap.c b/runcap.c
index de2f3a6..bbe735f 100644
--- a/runcap.c
+++ b/runcap.c
@@ -346,6 +346,7 @@ runcap_loop(struct runcap *rc)
if (rc->rc_pid == (time_t) -1)
kill(rc->rc_pid, SIGKILL);
+ rc->rc_errno = ETIMEDOUT;
tvp = &tv;
diff --git a/runcap.h b/runcap.h
index 9537eee..3ed907d 100644
--- a/runcap.h
+++ b/runcap.h
@@ -28,7 +28,7 @@ struct stream_capture
size_t sc_nlines; /* number of captured lines */
int sc_storfd; /* Storage file descriptor */
- void (*sc_linemon) (const char *, size_t, void *);
+ void (*sc_linemon)(const char *, size_t, void *);
/* Line monitor function */
void *sc_monarg; /* Line monitor argument */
@@ -49,7 +49,7 @@ struct runcap
unsigned rc_timeout; /* [IN] Execution timeout */
struct stream_capture rc_cap[RUNCAP_NBUF];
/* rc_cap[RUNCAP_STDIN] - [IN], rest - [OUT] */
- pid_t rc_pid; /* [OUT] - PID of the process */
+ pid_t rc_pid; /* PID of the process */
int rc_status; /* [OUT] - Termination status */
int rc_errno; /* [OUT] - Value of errno, if terminated on error */
@@ -69,7 +69,7 @@ int runcap(struct runcap *rc, int flags);
void runcap_free(struct runcap *rc);
static inline struct stream_capture *
-runcap_stream_capture(struct runcap *rc, int stream)
+runcap_get_capture(struct runcap *rc, int stream)
struct stream_capture *fp;
diff --git a/seek.c b/seek.c
index 2ff6b48..dfa3d1d 100644
--- a/seek.c
+++ b/seek.c
@@ -25,7 +25,7 @@ runcap_seek(struct runcap *rc, int sd, off_t off, int whence)
struct stream_capture *cap;
off_t cur;
- cap = runcap_stream_capture(rc, sd);
+ cap = runcap_get_capture(rc, sd);
if (!cap)
return -1;
diff --git a/tell.c b/tell.c
index 3b7a048..d16110c 100644
--- a/tell.c
+++ b/tell.c
@@ -25,7 +25,7 @@ runcap_tell(struct runcap *rc, int sd)
struct stream_capture *cap;
off_t off;
- cap = runcap_stream_capture(rc, sd);
+ cap = runcap_get_capture(rc, sd);
if (!cap)
return -1;

Return to:

Send suggestions and report system problems to the System administrator.