summaryrefslogtreecommitdiffabout
path: root/README
blob: 2ccdb2489d222de97cc5b0b7a56ba3290f424904 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
* Overview

The function *runcap* runs an external command, and waits for its
termination, optionally capturing its standard output and standard
error streams, and piping data to its standard input.

Upon return from the function, the caller can obtain the termination
status of the program and access the captured output data.

* Usage

The function *runcap* is defined as follows:

#+BEGIN_SRC  C
int runcap(struct runcap *rc, int flags);
#+END_SRC

The =rc= argument points to the structure that controls the execution
of the program.  It contains the input and output members.  The only 
member of this structure that must be initialized on input is
=rc_argv=, which points to the array of pointers to 
null-terminated strings that represent the command name and the 
argument list to that program.  Initialization of the rest of input
members is optional.  For each input member, there is a corresponding
flag in =flags= which must be set, if the member is initialized.  For
example, if the =rc_timeout= member is set (indicating maximum time
the program execution is allowed to take), then the =RCF_TIMEOUT= flag
must be set.

Upon return, the function returns the execution status of the
program, and initializes the output members of =rc= to hold the
standard output and standard error streams captured from the command.
Special functions are provided to read these streams.

For a detailed description of *runcap* and accompanying functions,
please see the [[http://man.gnu.org.ua/manpage/?3+runcap][runcap]](3) manual.  In this chapter we will illustrate
the usage of the *runcap* library by examples.

The example below defines the function *runcom* with the following
prototype:

#+BEGIN_SRC  C
int runcom(char *cmd, char *in, char **out, char **err);
#+END_SRC

The function runs the command line given in the ~cmd~ argument 
using =/bin/sh= and returns the data it printed on its standard output
and error streams in the memory locations pointed to by the arguments
~out~ and ~err~, correspondingly.  If any of these arguments is
=NULL=, capturing of the corresponding stream will be disabled.

The function returns program exit status on success, -1 if the program
terminated on signal and -2 on error.  This example implements only 
rudimentary error handling, in order to minimize the amount of
irrelevant code.

#+BEGIN_SRC  C
int runcom(char *cmd, char *in, char **out, char **err)
{
    int status;
    char *p;
    char c;
    char *argv[] = { "/bin/sh", "-c", cmd, NULL };
    struct runcap rc;
    int rcflags = RCF_TIMEOUT;

    /* Declare the command line to be run.  The rc_argv filed is the
     * only field that must be initialized on input.
     */
    rc.rc_argv = argv;

    /* Set maximum execution timeout.  The presense of this setting is
     * indicated by the RCF_TIMEOUT flag in rcflags. 
     */
    rc.rc_timeout = 10;

    /* If the input string is supplied, initialize the input stream and
     * raise the RCF_STDIN flag to indicate that it has been initialized.
     */
    if (in) {
	rc.rc_cap[RUNCAP_STDIN].sc_base = in;
	rc.rc_cap[RUNCAP_STDIN].sc_size = strlen(in);
	rc.rc_cap[RUNCAP_STDIN].sc_fd = -1;
	rcflags |= RCF_STDIN;
    }

    /* If out argument is NULL, disable capturing program's stdout.  To
     * disable capturing a stream, it suffices to initialize its sc_size
     * field to zero and raise the corresponding RCF_*_SIZE bit in flags.
     */
    if (!out) {
        rc.rc_cap[RUNCAP_STDOUT].sc_size = 0;
        rcflags |= RCF_STDOUT_SIZE;
    }

    /* Same for the stderr: */
    if (!err) {
        rc.rc_cap[RUNCAP_STDERR].sc_size = 0;
        rcflags |= RCF_STDERR_SIZE;
    }

    /* Run the command.  The runcap function returns 0 on success.  On
     * error, it returns -1 and sets the errno variable.  Its value is 
     * also duplicated in the rc_errno member of struct runcap.
     */
    if (runcap(&rc, rcflags)) {
	perror("runcap");
	return -2;
    }

    /* Upon return, the sc_leng member of the capturing structure for 
     * stdout and stderr contains total amount of bytes in the corresponding
     * stream. The stream can be read using the runcap_getc and 
     *  runcap_getline functions.
     */
    if (rc.rc_cap[RUNCAP_STDOUT].sc_leng) {
	p = malloc(rc.rc_cap[RUNCAP_STDOUT].sc_leng + 1);
	assert(p != NULL);
	*out = p;
	while (runcap_getc(&rc, RUNCAP_STDOUT, &c))
	    *p++ = c;
	*p = 0;
    } else
	*out = NULL;

    if (rc.rc_cap[RUNCAP_STDERR].sc_leng) {
	p = malloc(rc.rc_cap[RUNCAP_STDERR].sc_leng + 1);
	assert(p != NULL);
	*err = p;
	while (runcap_getc(&rc, RUNCAP_STDERR, &c))
	    *p++ = c;
	*p = 0;
    } else
	*err = NULL;

    /* Analyze the exit status of the command */
    if (WIFEXITED(rc.rc_status)) {
	status = WEXITSTATUS(rc.rc_status);
    } else {
	status = -1;

	if (WIFSIGNALED(rc.rc_status)) {
	    fprintf(stderr, "%s terminated on signal %d\n",
		    argv[0], WTERMSIG(rc.rc_status));
	} else {
	    fprintf(stderr, "%s terminated with unrecognized status: %d\n",
		    argv[0], rc.rc_status);
	}
    }
    return status;
}
#+END_SRC

* Downloading

To clone the project from the repository, run

#+BEGIN_SRC shell-script
git clone git://git.gnu.org.ua/runcap.git
#+END_SRC

* Building

The project can be used either a standalone library, or as a shared
or static convenience library embedded in another project. If you
cloned the project from the git repository, you will need to
bootstrap it first. To do so, change to the =runcap= directory and 
run 
#+BEGIN_SRC shell-script
  autoreconf -I. -f -i -s
#+END_SRC

Use the =RUNCAP_BUILD= environment variable to indicate the type of
the build you need.  Allowed values are: 

- install :: Build standalone installable library (default).
- shared  :: Build shared convenience library.
- static  :: Build static convenience library.

Once bootstrapped, the project can be built with the usual sequence
of commands:

1. Configure the package
  #+BEGIN_SRC shell-script
    ./configure
  #+END_SRC
2. Build it
  #+BEGIN_SRC shell-script
    make
  #+END_SRC
3. If building installable library, install it (normally run as root).
  #+BEGIN_SRC shell-script
    make install
  #+END_SRC  
  This will install the files *libruncap.so* and *libruncap.a* to the
  system library directory, and the header file *runcap.h* to the
  system include directory.

* Incorporating as a submodule

To incorporate *runcap* to your project as a submodule, follow these
steps:

1. Change to your project's toplevel directory.
2. Clone the project.
  #+BEGIN_SRC shell-script
    git submodule add git://git.gnu.org.ua/runcap.git
    git submodule init
  #+END_SRC
3. Add it to *git* index:
  #+BEGIN_SRC shell-script
    git add runcap
  #+END_SRC
4. Add it to your toplevel *Makefile.am*. 
  #+BEGIN_SRC make
    ACLOCAL_AMFLAGS = -I runcap

    SUBDIRS = runcap
  #+END_SRC
5. Edit your *configure.ac*. Add the following line:
  #+BEGIN_SRC autoconf
    RUNCAP_SETUP
  #+END_SRC
6. Add the following to the *Makefile.am* file which builds the target
   that uses on the *runcap* library:
  #+BEGIN_SRC make
    AM_CPPFLAGS = @RUNCAP_INC@
    LDADD = @RUNCAP_LDADD@
  #+END_SRC

* RUNCAP_SETUP autoconf macro

The *RUNCAP_SETUP* macro initializes the *runcap* library. It should
be used in the *configure.ac* file or in one of the files included to 
it.  The macro invocation syntax is:

#+BEGIN_SRC autoconf
RUNCAP_SETUP(DIR, TYPE)
#+END_SRC

Both arguments are optional:

- DIR  :: Name of the subdirectory where the *runcap* sources
          reside. If omitted, =runcap= is assumed. When
          building *runcap* as a standalone library, it is set to *.*
          (a dot).
- TYPE :: Build type: =install=, =shared=, or =static=.  Defaults to
          =static=.

This macro defines the following *make* variables:

- RUNTIME_INC        :: *cpp* options to access the =runcap.h=
                        include file.  Use it in the convenient
                        =_CPPFLAGS= *make* variable. 
- RUNCAP_LDADD       :: Lists the pathname of the *runcap*
                        library. Use it in the =LDADD=
                        or =prog_LDADD= *make* variable.
- RUNCAP_BUILD_TYPE  :: Type of the build.


* Copyright

Copyright (C) 2017-2019 Sergey Poznyakoff

Permission is granted to anyone to make or distribute verbatim copies
of this document as received, in any medium, provided that the
copyright notice and this permission notice are preserved,
thus giving the recipient permission to redistribute in turn.

Permission is granted to distribute modified versions
of this document, or of portions of it,
under the above conditions, provided also that they
carry prominent notices stating who last changed them.

* Document settings :noexport:

Please ignore this section. It supplies the variables necessary for
proper rendering of this document.

:PROPERTIES:
:VISIBILITY: folded
:END:

#+TITLE: runcap
#+STARTUP: showall
#+EXCLUDE_TAGS: noexport
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style1.css" />
#+OPTIONS: ^:nil

# Local Variables:
# mode: org
# paragraph-separate: "[ 	^L]*$"
# version-control: never
# End:

Return to:

Send suggestions and report system problems to the System administrator.