here's my whole code first :
1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <unistd.h>
4. #include <sys/wait.h>
5. #include <string.h>
6. int main(int argc, char *argv[]) {
7. int p[2]; // p[0]: file descriptor for read end of pipe
8. // p[1]: file descriptor for write end of pipe
9. if (pipe(p) < 0) exit(1);
10. int rc1 = fork();
11. if (rc1 < 0){ fprintf(stderr, "fork error\n"); }
12. else if (rc1 == 0){ write(p[1], "1st child output",
13. sizeof("1st child output")); }
14. else{
15. int rc2 = fork();
16. if (rc2 < 0){ fprintf(stderr, "fork error\n"); }
17. else if (rc2 == 0){
18. printf("2st child output\n");
19. char *_1st_child_out;
20. read(p[0], _1st_child_out, sizeof("1st child output"));
21. strcat(_1st_child_out, ", AFTER PIPE YA FOOL");
22. printf("%s\n", _1st_child_out);
23. }
24. }
25. }
if i initialize 19:13:
char *_1st_child_out;
with a '\0' or NULL, the string stays empty and 22:13:
printf("%s\n", _1st_child_out);
prints nothing, so how do strcat() and read() work? should i not insert any null terminators before invoking them? what about garbage values?
There are few bugs in your code, here are the my observation.
Case 1 :- In your code you are calling fork()
two times, pipe write end p[1]
contains some data 1st child output
in first fork()
rc1
process, but your code tried to read form p[0]
in second rc2
process.
you should check read()
return value whether it's success or not or may be it's reading from wrong/uninitialized file descriptors. This
char *_1st_child_out = NULL;
/* for process rc2, p[0] contains nothing, so what read() will read from p[0] ?? */
int ret = read(p[0], _1st_child_out, sizeof("1st child output"));
if(ret == -1) {
perror("read");
/* error handling */
}
Since data written into p[1]
in rc1
process, not in rc2
process, but here when you tries to read from p[0]
it gives you
read: Bad address
Case 2 :- To overcome above problem one way is
int main(int argc, char *argv[]) {
int p[2]; // p[0]: file descriptor for read end of pipe
// p[1]: file descriptor for write end of pipe
if (pipe(p) < 0) exit(1);
int rc1 = fork();
if (rc1 < 0){ fprintf(stderr, "fork error\n"); }
else if (rc1 == 0){
write(p[1], "1st child output",sizeof("1st child output"));
}
else{
char *_1st_child_out = NULL;
/* read() will read from p[0] and store into _1st_child_out but _1st_child_out not holding any valid memory ? So it causes Undefined behavior */
int ret = read(p[0], _1st_child_out, sizeof("1st child output"));
if(ret == -1) {
perror("read");
/* error handling */
}
strcat(_1st_child_out, ", AFTER PIPE YA FOOL");
printf("%s\n", _1st_child_out);
}
return 0;
}
Here _1st_child_out
is a pointer and pointers should have valid memory location. you can initialize with NULL
which is (void*)0
, that's valid but not with \0
as it's just a single character.
But when you initialize _1st_child_out
with NULL
and read the data from p[0]
& store into _1st_child_out
, what it will store into it ? It causes segmentation fault and it's also undefined behavior.
So it's better to allocate memory dynamically for _1st_child_out
and then call read()
or create the stack allocated array like
char _1st_child_out[10];
Here is the sample working code
int main(int argc, char *argv[]) {
int p[2]; // p[0]: file descriptor for read end of pipe
// p[1]: file descriptor for write end of pipe
if (pipe(p) < 0) exit(1);
int rc1 = fork();
if (rc1 < 0){ fprintf(stderr, "fork error\n"); }
else if (rc1 == 0){
write(p[1], "1st child output",sizeof("1st child output"));
}
else{
char *_1st_child_out = malloc(BYTE); /* define BYTE value as how much memory needed, and free the dynamically allocated memory once job is done */
int ret = read(p[0], _1st_child_out, sizeof("1st child output"));
if(ret == -1) {
perror("read");
/* error handling */
}
/* make sure _1st_child_out has enough memory space to concatenate */
strcat(_1st_child_out, ", AFTER PIPE YA FOOL");
printf("%s\n", _1st_child_out);
}
return 0;
}
Note : Use strncat()
instead of strcat()
, reason you can find from manual page of strcat()
https://linux.die.net/man/3/strcat It says
The
strcat()
function appends thesrc
string to thedest
string, overwriting the terminating null byte ('\0'
) at the end ofdest
, and then adds a terminating null byte. The strings may not overlap, and thedest
string must have enough space for the result. Ifdest
is not large enough, program behavior is unpredictable; buffer over‐ runs are a favorite avenue for attacking secure programs.The strncat() function is similar, except that * it will use at most n bytes from src; and * src does not need to be null-terminated if it contains n or more bytes. As with `strcat()`, the resulting string in dest is always null-terminated.