Reputation:
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?
Upvotes: 4
Views: 7319
Reputation: 239041
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).
Upvotes: 3
Reputation: 3082
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.
Upvotes: 1
Reputation: 11916
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.
Upvotes: 0
Reputation: 53861
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.
Upvotes: 6