tags:

views:

104

answers:

7

I have a parent process which forks out a child to perform execv().

I need the output given by execv() to stdout to be displayed onscreen as also copied to a log file.

How do I write the same output to both stdout & a file, without using pipes or tees?

+2  A: 

Pipe it through tee.

Nathon
is there a way to do this without pipes or tees, using just the dup() family?
Kedar Soparkar
+3  A: 

If you don't want to use a tee, before you write the data, write it to a file, then send it to stdout.

You should write a logging function that does this for you to make it cleaner.

Byron Whitlock
A: 

Do you want only the output of the child to go to the log?

The tee unix command does exactly what you describe: you pipe in data and it writes to a log and to stdout.

pydave
+1  A: 

Also, you can use fifo's. mkfifo my.fifo; in execv: program > my.fifo; and open fifo as a regular file, reading from it. This way you can have your stdout parsed, but it has minor drawbacks with shared access.

mhambra
+3  A: 

You can use dup2() - this link provides an example

DVK
Thats pretty ugly, forking for every printf? Sounds suboptimal to me....
Byron Whitlock
That link provides an example where stdout is redirected to a file and then redirected back to normal stdout. The poster was after a way for a normal print statement to be written to *both* stdout and a file.
ar
@Byron - the fork is not there for printing purposes, just part of the logic of the script that has an example. the post down the same thread has pure logic with no `fork`: http://www.linuxforums.org/forum/linux-programming-scripting/69391-redirect-stdout-file.html#post363639
DVK
A: 

Use Tee like this:

./myprog | tee outputfile
Taylor Leese
Something that doesn't involve calling an execv() please...
Kedar Soparkar
+1  A: 

You can do this entirely within your program, but you will still need to use anonymous pipes as created by the pipe() system call.

Basically, you will need a subprocess that performs the equivalent of tee, which is quite easy:

int child_pipe[2];
pid_t pid_exec_child, pid_output_child;

pipe(child_pipe);

pid_exec_child = fork();
if (pid_exec_child == 0)
{
    dup2(child_pipe[1], STDOUT_FILENO);
    close(child_pipe[0]);
    close(child_pipe[1]);
    execve(/* ... */);
    _exit(127);
}

close(child_pipe[1]);

pid_output_child = fork();
if (pid_output_child == 0)
{
    /* This child just loops around, reading from the other child and writing everything
     * to both stdout and the log file. */
    int logfd = open("logfile", O_WRONLY);
    char buffer[4096];
    ssize_t nread;

    while ((nread = read(child_pipe[0], buffer, sizeof buffer) != 0)
    {
        size_t nwritten_total;
        ssize_t nwritten;

        if (nread < 0)
        {
            if (errno == EINTR)
                continue;

            perror("read");
            _exit(1);
        }

        /* Copy data to stdout */
        nwritten_total = 0;
        while (nwritten_total < nread)
        {
            nwritten = write(STDOUT_FILENO, buffer + nwritten_total, nread - nwritten_total);

            if (nwritten < 0)
            {
                if (errno == EINTR)
                    continue;

                perror("write(stdout)");
                _exit(2);
            }

            nwritten_total += nwritten;
        }

        /* Copy data to logfile */
        nwritten_total = 0;
        while (nwritten_total < nread)
        {
            nwritten = write(logfd, buffer + nwritten_total, nread - nwritten_total);

            if (nwritten < 0)
            {
                if (errno == EINTR)
                    continue;

                perror("write(logfile)");
                _exit(3);
            }

            nwritten_total += nwritten;
        }
    }
    _exit(0);
}

close(child_pipe[0]);

/* Parent continues here */

Of course, it is probably easier to just exec tee in that second child instead...

(Note that the child processes use _exit(), since they have inherited the standard I/O state from the parent).

caf