Gregory Hildstrom Projects Publications Resume Links Contact About Google+ Facebook Youtube Donate




Capture Child Process Output With Pipes

Introduction
capture_child_output.c
Makefile

Introduction

Some recent tasks at work highlighted some obvious gaps in my working knowledge of Linux pipes. I spent some personal time brushing up and learning. This page addresses how to exec a child process and capture its standard output and standard error in the parent process. I show the example program and output, but the code is also available in
pipeoutput.zip.

The man pages are extremely helpful when diving into stuff like this, but I definitely hit a wall and resorted to some Internet searching. I came across a very helpful page, as noted in the code, and I incorporated some ideas from that page in this example.

My test system runs Fedora 20 Linux with the latest updates as of August 9, 2015. The Makefile at the end shows the compile commands.

capture_child_output.c

/*
 * This demonstrates capturing child process standard output using pipe IPC.
 *
 * This page is a useful supplement to the man pages.
 * http://www.microhowto.info/howto/capture_the_output_of_a_child_process_in_c.html
 */

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <malloc.h>

int main(int argc, char **argv)
{
    int rc = 0;
    pid_t cpid = 0;
    int filedes[2] = {0,0};
    char *buffer = NULL;
    size_t buffer_size = 4096;
    ssize_t bytes_read = 0;
    if (pipe(filedes) < 0) {
        perror("pipe");
        goto cleanup;
    }
    cpid = fork();
    if (cpid < 0) {
        perror("fork");
        goto cleanup;
    }
    else if (cpid == 0) {
        printf("child pid is %d\n", getpid());
        while ((dup2(filedes[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
        while ((dup2(filedes[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
        close(filedes[1]);
        close(filedes[0]);
        printf("hello from client ");
        write(STDERR_FILENO, "error from client ", strlen("error from client "));
    }
    else {
        printf("parent pid is %d and child pid is %d\n", getpid(), cpid);
        close(filedes[1]);
        buffer = (char*)malloc(buffer_size);
        if (!buffer) {
            perror("malloc");
            goto cleanup;
        }
        while (1) {
            bytes_read = read(filedes[0], buffer, buffer_size);
            if (bytes_read < 0) {
                if (errno == EINTR)
                    continue;
                else {
                    perror("read");
                    goto cleanup;
                }
            }
            else if (bytes_read == 0)
                break;
            else {
                printf("parent received output from child: %s\n", buffer);
            }
        }
        close(filedes[0]);
        wait(0);
    }
    rc = 0;
cleanup:
    free(buffer);
    return rc;
}

Here is the output:
[ghildstrom@hplt pipeoutput]$ ./capture_child_output 
parent pid is 7391 and child pid is 7392
child pid is 7392
parent received output from child: error from client hello from client 

[ghildstrom@hplt pipeoutput]$ ./capture_child_output 
parent pid is 7413 and child pid is 7414
child pid is 7414
parent received output from child: error from client 
parent received output from child: hello from client

Makefile

all: capture_child_output

capture_child_output: capture_child_output.c
	gcc -o capture_child_output capture_child_output.c

clean:
	rm -f capture_child_output