2 This program is free software; you can redistribute it and/or modify
3 it under the terms of the GNU General Public License as published by
4 the Free Software Foundation; either version 2 of the License, or
5 (at your option) any later version.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 Roland Illig <roland.illig@gmx.de>, 2004.
20 /** \file pipethrough.c
21 * \brief Source: a module for piping
23 * Pipe a buffer through a child command and receive two containing
24 * the stdout and stderr output of the child process.
35 #include <string.h> /* On Solaris, FD_SET invokes memset(3) */
37 #include <sys/types.h>
40 #ifdef HAVE_SYS_SELECT_H
41 # include <sys/select.h>
45 #include "pipethrough.h"
50 /* Internal data types *************************************************/
52 struct writer_buffer
{
53 /*@temp@*/ const void *data
;
60 struct reader_buffer
{
61 /*@null@*/ /*@only@*/ void *data
;
68 struct select_request
{
74 /* Helper functions ****************************************************/
76 static int closeref(int *fd
)
77 /*@globals internalState, fileSystem, errno; @*/
78 /*@modifies internalState, fileSystem, errno, *fd; @*/
80 int result
= close(*fd
);
85 static void propagate(int *dest
, int src
)
86 /*@modifies *dest; @*/
93 /* reader_buffer -- implementation *************************************/
95 static void reader_buffer_init(/*@out@*/ struct reader_buffer
*buf
, int fd
)
105 /* SPlint cannot describe realloc(3) correctly. */
106 /*@-usereleased@*/ /*@-compdef@*/ /*@-branchstate@*/ /*@-compmempass@*/
107 static ssize_t
reader_buffer_read(struct reader_buffer
*buf
)
108 /*@globals internalState, errno; @*/
109 /*@modifies internalState, errno, *buf; @*/
113 if (buf
->size
== buf
->maxsize
) {
114 size_t newsize
= buf
->maxsize
+ 4096;
115 /*@only@*/ void *newdata
;
116 if (buf
->data
== NULL
) {
117 newdata
= malloc(newsize
);
119 newdata
= realloc(buf
->data
, newsize
);
121 if (newdata
== NULL
) {
126 buf
->maxsize
= newsize
;
128 res
= read(buf
->fd
, &(((char *) buf
->data
)[buf
->size
]),
129 buf
->maxsize
- buf
->size
);
130 if (res
== (ssize_t
) -1) {
133 buf
->size
+= (ssize_t
) res
;
136 /*@=usereleased@*/ /*@=compdef@*/ /*@=branchstate@*/ /*@=compmempass@*/
138 static void reader_buffer_handle(struct reader_buffer
*buf
)
139 /*@globals internalState, fileSystem, errno; @*/
140 /*@modifies internalState, fileSystem, errno, *buf; @*/
142 ssize_t rd
= reader_buffer_read(buf
);
143 if (rd
== (ssize_t
) -1) {
144 propagate(&buf
->lasterror
, errno
);
147 if (closeref(&(buf
->fd
)) == -1) {
148 propagate(&buf
->lasterror
, errno
);
154 static void reader_buffer_finalize(/*@special@*/ struct reader_buffer
*buf
)
155 /*@modifies *buf; @*/
156 /*@releases buf->data; @*/
158 if (buf
->data
!= NULL
) {
168 /* writer_buffer -- implementation *************************************/
170 static void writer_buffer_init(/*@out@*/ struct writer_buffer
*buf
, int fd
,
171 const void *data
, size_t size
)
172 /*@globals internalState, fileSystem, errno; @*/
173 /*@modifies internalState, fileSystem, errno, *buf; @*/
181 if (closeref(&(buf
->fd
)) == -1) {
182 propagate(&buf
->lasterror
, errno
);
187 static void writer_buffer_handle(struct writer_buffer
*buf
)
188 /*@globals internalState, fileSystem, errno; @*/
189 /*@modifies internalState, fileSystem, errno, *buf; @*/
191 typedef void (*my_sighandler_fn
) (int);
192 my_sighandler_fn old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
193 ssize_t wr
= write(buf
->fd
, &(((const char *) buf
->data
)[buf
->pos
]),
194 buf
->size
- buf
->pos
);
195 if (wr
== (ssize_t
) -1) {
196 propagate(&buf
->lasterror
, errno
);
200 if (wr
== (ssize_t
) -1 || buf
->pos
== buf
->size
) {
201 if (closeref(&(buf
->fd
)) == -1) {
202 propagate(&buf
->lasterror
, errno
);
205 (void) signal(SIGPIPE
, old_sigpipe
);
208 static void writer_buffer_finalize(/*@special@*/ struct writer_buffer
*buf
)
209 /*@modifies *buf; @*/
217 /* select_request -- implementation ************************************/
219 static void select_request_init(/*@out@*/ struct select_request
*sr
)
222 FD_ZERO(&(sr
->readfds
));
223 FD_ZERO(&(sr
->writefds
));
227 static void select_request_add_reader(struct select_request
*sr
,
228 const struct reader_buffer
*buf
)
232 FD_SET(buf
->fd
, &(sr
->readfds
));
233 if (buf
->fd
+ 1 > sr
->n
) {
239 static void select_request_add_writer(struct select_request
*sr
,
240 const struct writer_buffer
*buf
)
244 FD_SET(buf
->fd
, &(sr
->writefds
));
245 if (buf
->fd
+ 1 > sr
->n
) {
251 static void select_request_handle_writer(const struct select_request
*sr
,
252 struct writer_buffer
*buf
)
253 /*@globals internalState, fileSystem, errno; @*/
254 /*@modifies internalState, fileSystem, errno, *buf; @*/
256 if (buf
->fd
!= -1 && FD_ISSET(buf
->fd
, &(sr
->writefds
))) {
257 writer_buffer_handle(buf
);
261 static void select_request_handle_reader(const struct select_request
*sr
,
262 struct reader_buffer
*buf
)
263 /*@globals internalState, fileSystem, errno; @*/
264 /*@modifies internalState, fileSystem, errno, *buf; @*/
266 if (buf
->fd
!= -1 && FD_ISSET(buf
->fd
, &(sr
->readfds
))) {
267 reader_buffer_handle(buf
);
271 /* pipethrough -- helper functions *************************************/
273 static void pipethrough_child(const char *command
,
274 const int *stdin_pipe
,
275 const int *stdout_pipe
,
276 const int *stderr_pipe
)
277 /*@globals internalState, fileSystem, errno, stderr; @*/
278 /*@modifies internalState, fileSystem, errno, *stderr; @*/
280 const char *shell
= getenv("SHELL");
285 if (close(STDIN_FILENO
) == -1
286 || dup2(stdin_pipe
[PIPE_RD
], STDIN_FILENO
) == -1
287 || close(stdin_pipe
[PIPE_RD
]) == -1
288 || close(stdin_pipe
[PIPE_WR
]) == -1
289 || close(STDOUT_FILENO
) == -1
290 || dup2(stdout_pipe
[PIPE_WR
], STDOUT_FILENO
) == -1
291 || close(stdout_pipe
[PIPE_RD
]) == -1
292 || close(stdout_pipe
[PIPE_WR
]) == -1
293 || close(STDERR_FILENO
) == -1
294 || dup2(stderr_pipe
[PIPE_WR
], STDERR_FILENO
) == -1
295 || close(stderr_pipe
[PIPE_RD
]) == -1
296 || close(stderr_pipe
[PIPE_WR
]) == -1
297 || execl(shell
, shell
, "-c", command
, NULL
) == -1) {
298 perror("pipethrough_child");
299 (void) fflush(stderr
);
304 static int pipethrough_parent(int stdin_fd
,
307 const struct pipe_inbuffer
*stdin_buf
,
308 /*@out@*/ struct pipe_outbuffer
*stdout_buf
,
309 /*@out@*/ struct pipe_outbuffer
*stderr_buf
)
310 /*@globals internalState, fileSystem, errno; @*/
311 /*@modifies internalState, fileSystem, errno,
312 *stdout_buf, *stderr_buf; @*/
314 struct writer_buffer stdin_wbuf
;
315 struct reader_buffer stdout_rbuf
;
316 struct reader_buffer stderr_rbuf
;
317 struct select_request sr
;
322 writer_buffer_init(&stdin_wbuf
, stdin_fd
, stdin_buf
->data
,
324 propagate(&firsterror
, stdin_wbuf
.lasterror
);
325 reader_buffer_init(&stdout_rbuf
, stdout_fd
);
326 propagate(&firsterror
, stdout_rbuf
.lasterror
);
327 reader_buffer_init(&stderr_rbuf
, stderr_fd
);
328 propagate(&firsterror
, stdout_rbuf
.lasterror
);
330 while (stdin_wbuf
.fd
!= -1 || stdout_rbuf
.fd
!= -1
331 || stderr_rbuf
.fd
!= -1) {
332 select_request_init(&sr
);
335 select_request_add_writer(&sr
, &stdin_wbuf
);
336 select_request_add_reader(&sr
, &stdout_rbuf
);
337 select_request_add_reader(&sr
, &stderr_rbuf
);
338 select_res
= select(sr
.n
, &(sr
.readfds
), &(sr
.writefds
),
340 if (select_res
== -1) {
341 if (errno
== EINTR
) {
344 propagate(&firsterror
, errno
);
348 select_request_handle_writer(&sr
, &stdin_wbuf
);
349 propagate(&firsterror
, stdin_wbuf
.lasterror
);
350 select_request_handle_reader(&sr
, &stdout_rbuf
);
351 propagate(&firsterror
, stdout_rbuf
.lasterror
);
352 select_request_handle_reader(&sr
, &stderr_rbuf
);
353 propagate(&firsterror
, stdout_rbuf
.lasterror
);
355 stdout_buf
->data
= stdout_rbuf
.data
;
356 stdout_rbuf
.data
= NULL
;
357 stdout_buf
->size
= stdout_rbuf
.size
;
358 stderr_buf
->data
= stderr_rbuf
.data
;
359 stderr_rbuf
.data
= NULL
;
360 stderr_buf
->size
= stderr_rbuf
.size
;
364 writer_buffer_finalize(&stdin_wbuf
);
365 reader_buffer_finalize(&stdout_rbuf
);
366 reader_buffer_finalize(&stderr_rbuf
);
371 /* pipethrough -- implementation ***************************************/
373 extern int pipethrough(const char *command
,
374 const struct pipe_inbuffer
*stdin_buf
,
375 /*@out@*/ struct pipe_outbuffer
*stdout_buf
,
376 /*@out@*/ struct pipe_outbuffer
*stderr_buf
,
377 /*@out@*/ int *status
)
378 /*@globals internalState, fileSystem, errno, stderr; @*/
379 /*@modifies internalState, fileSystem, errno, *stderr,
380 *stdout_buf, *stderr_buf, *status; @*/
384 int stdin_pipe
[2] = { -1, -1 };
385 int stdout_pipe
[2] = { -1, -1 };
386 int stderr_pipe
[2] = { -1, -1 };
389 if (pipe(stdin_pipe
) == -1
390 || pipe(stdout_pipe
) == -1
391 || pipe(stderr_pipe
) == -1) {
392 propagate(&firsterror
, errno
);
396 if ((pid
= fork()) == (pid_t
) -1) {
397 propagate(&firsterror
, errno
);
402 pipethrough_child(command
, stdin_pipe
, stdout_pipe
,
407 if (closeref(&stdin_pipe
[PIPE_RD
]) == -1
408 || closeref(&stdout_pipe
[PIPE_WR
]) == -1
409 || closeref(&stderr_pipe
[PIPE_WR
]) == -1) {
410 propagate(&firsterror
, errno
);
414 if (pipethrough_parent(stdin_pipe
[PIPE_WR
], stdout_pipe
[PIPE_RD
],
415 stderr_pipe
[PIPE_RD
], stdin_buf
,
416 stdout_buf
, stderr_buf
) == -1) {
417 propagate(&firsterror
, errno
);
423 if (waitpid(pid
, status
, 0) == (pid_t
) -1) {
424 propagate(&firsterror
, errno
);
425 pipe_outbuffer_finalize(stdout_buf
);
426 pipe_outbuffer_finalize(stderr_buf
);
432 (void) close(stderr_pipe
[PIPE_RD
]);
433 (void) close(stderr_pipe
[PIPE_WR
]);
434 (void) close(stdout_pipe
[PIPE_RD
]);
435 (void) close(stdout_pipe
[PIPE_WR
]);
436 (void) close(stdin_pipe
[PIPE_RD
]);
437 (void) close(stdin_pipe
[PIPE_WR
]);
444 /* pipe_outbuffer -- implementation ************************************/
446 /*@-mustfreeonly@*/ /*@-nullstate@*/
447 extern void pipe_outbuffer_finalize(struct pipe_outbuffer
*buf
)
448 /*@modifies *buf; @*/
449 /*@releases buf->data; @*/
450 /*@ensures isnull buf->data; @*/
452 if (buf
->data
!= NULL
) {
458 /*@=mustfreeonly@*/ /*@=nullstate@*/