I attempt to pipe the output of a child, into a file and write it by Father.
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int prepare_log(void){
int fd = open("error.log",
O_CREAT | O_RDWR | O_APPEND,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
return fd;
}
void log_stderr(int pipe_ends[], int outfile){
close(pipe_ends[1]);
dup2(pipe_ends[0],outfile);
close(pipe_ends[0]);
}
void child(int pipe_ends[], char* argv[]){
close(pipe_ends[0]);
dup2(pipe_ends[1],1);
close(pipe_ends[1]);
execvp(argv[0], argv);
}
void bury(){
int status;
wait(0);
}
int main(){
int fd = prepare_log();
char* argv[] = {"seq", "10", NULL};
int pipe1[2];
if(pipe(pipe1) == -1){
printf("Dont' create Pipe\n");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if(pid < 0){
perror("ERROR");
exit(EXIT_FAILURE);
} else if(pid > 0){
log_stderr(pipe1, fd);
bury();
} else if (pid == 0){
child(pipe1,argv);
}
}
At the moment I try only to pipe the output of child to father and then write it to a file. My final goal is also to display it to the terminal. My idea is to use 3 pipes and redirects the first pipe that we can see in the code, as the input of the 2-nd and 3-rd pipe. Then redirect the output of the 2-nd pipe with dup2(pipe2[1],file1) und the 3-rd with dup2(pipe2[1], stdout).
The parent process is going to have to read the response from the child, and arrange to write that information twice, once to the log file and once to the terminal. Or you're going to have to arrange for the output from the child to go to a program like tee
which writes copies of its input to multiple destinations.
More pipes won't help unless you're using one to redirect the output of the child to tee
.
You aren't closing enough file descriptors in the child process.
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both of the
original file descriptors returned by
pipe()
as soon as possible.
In particular, you should close them before using any of the
exec*()
family of functions.
The rule also applies if you duplicate the descriptors with either
dup()
or
fcntl()
with F_DUPFD
or F_DUPFD_CLOEXEC
.
If the parent process will not communicate with any of its children via
the pipe, it must ensure that it closes both ends of the pipe early
enough (before waiting, for example) so that its children can receive
EOF indications on read (or get SIGPIPE signals or write errors on
write), rather than blocking indefinitely.
Even if the parent uses the pipe without using dup2()
, it should
normally close at least one end of the pipe — it is extremely rare for
a program to read and write on both ends of a single pipe.
Note that the O_CLOEXEC
option to
open()
,
and the FD_CLOEXEC
and F_DUPFD_CLOEXEC
options to fcntl()
can also factor
into this discussion.
If you use
posix_spawn()
and its extensive family of support functions (21 functions in total),
you will need to review how to close file descriptors in the spawned process
(posix_spawn_file_actions_addclose()
,
etc.).
Note that using dup2(a, b)
is safer than using close(b); dup(a);
for a variety of reasons.
One is that if you want to force the file descriptor to a larger than
usual number, dup2()
is the only sensible way to do that.
Another is that if a
is the same as b
(e.g. both 0
), then dup2()
handles it correctly (it doesn't close b
before duplicating a
)
whereas the separate close()
and dup()
fails horribly.
This is an unlikely, but not impossible, circumstance.
Here is a fixed version of your program. The logic in log_stderr()
is odd; the one thing it doesn't do is write to standard error. I've modified it so it does read from the pipe and write to both standard output and the log file. Note that it takes one read but two writes to replicate the information. Error checking is skimpy to non-existent. The bury()
function now reports on the death of a child. The code was saved in pipe71.c
and compiled to pipe71
.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static
int prepare_log(void)
{
int fd = open("error.log",
O_CREAT | O_RDWR | O_APPEND,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
return fd;
}
static
void log_stderr(int pipe_ends[], int outfile)
{
char buffer[512];
int nbytes;
close(pipe_ends[1]);
while ((nbytes = read(pipe_ends[0], buffer, sizeof(buffer))) > 0)
{
write(STDOUT_FILENO, buffer, nbytes);
write(outfile, buffer, nbytes);
}
close(pipe_ends[0]);
close(outfile);
}
static
void child(int pipe_ends[], char *argv[])
{
dup2(pipe_ends[1], 1);
close(pipe_ends[0]);
close(pipe_ends[1]);
execvp(argv[0], argv);
fprintf(stderr, "failed to execute %s\n", argv[0]);
exit(EXIT_FAILURE);
}
static
void bury(void)
{
int status;
int corpse;
while ((corpse = wait(&status)) > 0)
printf("%d: child %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);
}
int main(void)
{
int fd = prepare_log();
char *argv[] = {"seq", "10", NULL};
int pipe1[2];
pipe(pipe1);
pid_t pid = fork();
if (pid > 0)
{
log_stderr(pipe1, fd);
bury();
}
else if (pid == 0)
{
child(pipe1, argv);
}
return 0;
}
However, it does work:
$ rm error.log
$ ./pipe71
1
2
3
4
5
6
7
8
9
10
99989: child 99990 exited with status 0x0000
$ cat error.log
1
2
3
4
5
6
7
8
9
10
$