Using two pipes to communicate between parent process and child process - c

Problem
I only get this in the terminal output. I believe the program is getting stuck at the fork() call but I don't know exactly why.
The name of the program is q9:
prompt>$ ./q9 inputString
Parent: writing to pipe 'inputString'
Task
read input from terminal into the parent-to-child pipe.
fork() to create a child process.
read input from parent-to-child pipe.
concatenate some other string to that string read in from the pipe.
write the newly concatenated string to the child-to-parent pipe.
in the parent, read from the child-to-parent pipe and print the output read from the pipe to the terminal.
Attempts
I have tried fixing this by:
attempting to close pipes in different places. I thought I may have missed something or left something open, but I don't think so.
placing a wait() in the parent because perhaps it wasn't letting the child run completely
attempted to print the output of the concatenated string just in case it was that messing up the prints.
Code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
int main (int argc, char *argv[]) {
// parent RUN
if(argc == 1) {
printf("usage: q9 <string>\n");
return 0;
}
// create two way pipes
int parent_fds[2], child_fds[2];
// create strings to save too
char fromParent[100];
char fromChild[100];
// read:[0] - write:[1]
if (pipe(parent_fds) != 0 && pipe(child_fds) != 0) {
fprintf(stderr, "pipes failed!\n");
}
// close unused pipe end by parent
close(parent_fds[0]);
close(child_fds[1]);
close(child_fds[0]);
// write from terminal to parent pipe FOR child to read
printf("Parent: writing to pipe '%s'\n", argv[1]);
write(parent_fds[1], argv[1], strlen(argv[1]));
close(parent_fds[1]);
// fork() child process
int child = fork();
// NEVER GETS PASSED HERE :(
if (child < 0) {
fprintf(stderr, "fork failed!");
exit(1);
} else if (child == 0) {
printf("I reached the child :)");
// close unwanted pipe ends by child
close(child_fds[0]);
close(parent_fds[1]);
// read from parent pipe
int n = read(parent_fds[0], fromParent, 100);
fromParent[n] = 0;
printf("Child: reading from parent pipe '%s'\n", fromParent);
close(parent_fds[0]);
// Concatinate to what was read in
const char myText[14] = " (added this.)";
strcat(fromParent, myText);
write(child_fds[1], fromParent, strlen(fromParent));
close(child_fds[1]);
printf("Child: writing to pipe - '%s'\n", fromParent);
} else {
// read from child pipe
int n = read(child_fds[0], fromChild, 100);
fromChild[n] = 0;
printf("Parent: reading from pipe - '%s'\n", fromChild);
}
return 0;
}
What is going wrong?

There were several problems, and your diagnostic messages were not guaranteed to appear. Make sure you end your messages with newlines.
You only created one pipe because you used && instead of ||.
You closed the pipes 'for the parent' before you'd created the child (also noted by kaylum in a comment).
There are multiple other cleanups in the code below. The code (still) does not ensure that the write-to-pipe operations succeed (they were failing before). It does ensure that the strings read from the pipes are not longer than the buffers in which they are placed; it doesn't ensure there's enough space to append the extra information in the child. The code shown waits for any child processes to complete before exiting. The child executes the wait() call but it immediately fails (and the child doesn't print anything) and it exits. The parent waits for the child to complete and reports on it doing so before exiting.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if (argc == 1)
{
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
return EXIT_FAILURE;
}
// create two pipes:
// - parent_fds used by parent to write to child
// - child_fds used by child to write to parent
int parent_fds[2], child_fds[2];
// read:[0] - write:[1]
if (pipe(parent_fds) != 0 || pipe(child_fds) != 0) /* || not && */
{
fprintf(stderr, "pipes failed!\n");
return EXIT_FAILURE;
}
// fork() child process
int child = fork();
if (child < 0)
{
fprintf(stderr, "fork failed!");
return EXIT_FAILURE;
}
else if (child == 0)
{
printf("%d: I reached the child :)\n", (int)getpid());
// close unwanted pipe ends by child
close(child_fds[0]);
close(parent_fds[1]);
// read from parent pipe
char fromParent[100];
int n = read(parent_fds[0], fromParent, sizeof(fromParent) - 1);
fromParent[n] = '\0';
printf("%d: Child: read from parent pipe '%s'\n", (int)getpid(), fromParent);
close(parent_fds[0]);
// Append to what was read in
strcat(fromParent, " (added this.)");
write(child_fds[1], fromParent, strlen(fromParent));
close(child_fds[1]);
printf("%d: Child: writing to pipe - '%s'\n", (int)getpid(), fromParent);
}
else
{
// close unwanted pipe ends by parent
close(parent_fds[0]);
close(child_fds[1]);
// write from terminal to parent pipe FOR child to read
printf("%d: Parent: writing to pipe '%s'\n", (int)getpid(), argv[1]);
write(parent_fds[1], argv[1], strlen(argv[1]));
close(parent_fds[1]);
// read from child pipe
char fromChild[100];
int n = read(child_fds[0], fromChild, sizeof(fromChild) - 1);
fromChild[n] = '\0';
close(child_fds[0]);
printf("%d: Parent: read from pipe - '%s'\n", (int)getpid(), fromChild);
}
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("%d: child PID %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);
return EXIT_SUCCESS;
}
Sample output (source pipe43.c, program pipe43):
$ pipe43 'Nobody expects the Spanish Inquisition!'
84543: Parent: writing to pipe 'Nobody expects the Spanish Inquisition!'
84544: I reached the child :)
84544: Child: read from parent pipe 'Nobody expects the Spanish Inquisition!'
84544: Child: writing to pipe - 'Nobody expects the Spanish Inquisition! (added this.)'
84543: Parent: read from pipe - 'Nobody expects the Spanish Inquisition! (added this.)'
84543: child PID 84544 exited with status 0x0000
$

Related

Creating pipes in C for inter process communication between multiple processes

There are already multiple answers to this question but none of them have been able to help me solve my problem. I am trying to understand IPC using an anonymous pipe in C.
From my understanding of pipes, they are a one way communication channel with one read end and one write end.
Assuming we have two c files one named parent.c and the other child.c. What I am trying to achieve is to be able to create 5 or more child processes. After this the parent and the child should communicate with the child processes through standard input and standard output, but since I want to be able to print what the parent receives from the child I'll instead tie the pipes to standard error output using dup2.
In summary
1. Run a parent program which spins up 5 or more child processes and runs them.
2. The child process waits for an input from the parent using scanf.
3. The parent sends a message to the child process.
4. The child process receives the message and sends a reply to the parent and exits.
5. The parent process prints the received message and prints it then exits.
parent.c
// Parentc
#include <stdio.h>
#include <stdlib.h>
#include <uinstd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, const char *argv[]){
// File descriptors for the pipes
int read_pipe[2]; // From child to parent
int write_pipe[2]; // From parent to child
pid_t process_id;
int exit_status;
// Try to fork 5 child processes
for(int i = 0; i < 5; i++){
if(pipe(write_pipe) == -1 || pipe(read_pipe) == -1){
perror("Pipe");
exit(1);
}
// Spin a child process
process_id = fork();
if(process_id == -1){
perror("Fork");
exit(1);
} else if(processId == 0) {
// The child process
// I don't know what to do here, The idea is to close the
// unneeded end of the pipes and wait for input from the parent
// process
// Start the ./child
execl("./child", "");
} else {
// The parent process
char recieved_data[1024];
// Send data to child since stderr is duplicated in the pipe
// It sends the pid of the child
fprintf(stderr, "Test data to %d ", process_id);
// Wait to recieve data from child
// Don't know how to do that
// Print the recieved data
printf("Parent recieved: \"%s\"\n", recieved_data);
wait(&exit_status); // Will wait till all children exit before exiting
}
}
return 0;
}
The child.c is a simple program as shown below
child.c
#include <stdio.h>
int main(int argc, const char *argv[]){
char data_buffer[1024];
// Wait for input from parent
scanf("%s", data_buffer);
// Send data back to parent
printf("Child process: %s", data_buffer);
return 0;
}
Expected output
$ ./parent
parent recived: "Child process: Test data to 12345"
parent recived: "Child process: Test data to 12346"
parent recived: "Child process: Test data to 12347"
parent recived: "Child process: Test data to 12348"
parent recived: "Child process: Test data to 12349"
Where 12345, 12346....12349 is the process id of the child process
Here you have a code i did, and i will use to explain to you:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char buff[1024];
int aux, i, count;
int fds[2], fdss[2];
pipe(fds); //Here we initialize the file descriptors
pipe(fdss);
mode_t fd_mode = S_IRWXU;
for (i = 0; i < 3; i++) {
aux = fork();
if (aux == 0)
break;
}
switch (i) {
case 0:
printf("Write something:\n");
scanf("%s[^\n]", buff);
i = 0;
count = 0;
while(buff[i] != '\0') {
count++;
i++;
}
dup2(fds[1], 1);
close(fds[1]);
close(fds[0]);
close(fdss[0]);
close(fdss[1]);
write (1, buff, sizeof(buff));
break;
case 1:
dup2(fds[0], 0);
dup2(fdss[1], 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
//
if (execl("/bin/grep", "grep", "example", NULL) == -1) {
printf("Error\n");
exit (1);
}
break;
case 2:
aux = open("result.txt", O_RDWR | O_CREAT , S_IRWXU);
dup2(fdss[0], 0);
dup2(aux, 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
close(aux);
if (execl("/usr/bin/wc", "wc", "-l", NULL) == -1) {
printf("Error \n");
exit (1);
}
}
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
for (i = 0; i < 3; i++) wait(NULL);
return 0;
}
Ok, let's start:
We create and initialize pipes with pipe()
Then we write our code and before execl() we change the file descriptors, in order to pass the text we will write in the console, through processes and finally write in a file called result.txt the result of the "grep example" command applied to the text we have written.
The function dup2(new_descriptor, old_descriptor) is copying the new descriptor into the old descriptor and closes the old descriptor. For example:
Before dup2(fds[1], 1) we have:
0 STDIN
1 STDOUT
2 STDERR
After dup2(fds[1], 1) we have:
0 STDIN
1 fds[1]
2 STDERR
NOTE: If you don't want to use 1, yo can simply write STDOUT_FILENO
So now we are able to write through processes and in my example to a file too

How a child process kill other child process and then terminate?

Here is the code, where parent process writes a string input in pipe and children processes read this from pipe. If child process reads from pipe the word "end", then i want to terminate all the processes and then terminate itself, and if reads the word "finish" i want to raise a signal to father for killing all the processes and then exit. I run the code and i had segmentation fault. Why it is wrong?
#define _POSIX_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
void measure_time(int sig)
{
printf("child [%d] received signal %d\n", getpid(), sig);
}
int main(int argc, char *argv[])
{
int n_task = 4;
pid_t pid;
pid_t pid_array[n_task];
int fd[2];
for (int i = 0; i < n_task; i++)
{
pid = fork();
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
if (pid < 0)
{
perror("fork");
exit(1);
}
if (pid == 0) //child
{
char *buf;
close(fd[1]);
read(fd[0], buf, 10);
printf("I read: %s", buf);
if (strcmp(buf, "end") == 0)
{
for (int i = 0; i < n_task; i++)
kill(pid_array[i], SIGUSR1);
}else if(strcmp(buf,"finish") == 0){
/*Here i want father to kill all children and then exit.*/
}
exit(0);
}
close(fd[0]);
char *buf;
printf("Give the input string: \n");
scanf("%s", buf);
write(fd[1], buf, strlen(buf));
close(fd[1]);
pid_array[i] = pid;
}
sleep(1);
for (int i = 0; i < n_task; i++)
wait(NULL);
return (0);
}
Besides the issue of uninitialized buf identified by #G. Sliepen, the pipe() need be called before fork() as file descriptors are kept open when forking child process(s). This is also how pipe works.
You can try to change your code snippet to put pipe() before fork().
...
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
pid = fork();
if (pid < 0)
{
perror("fork");
exit(1);
}
...
Please read the manual page of pipe(2) in which an example presented.
SO has this post fork() and pipes() in c explained this as well.
Update for terminating process(s)
This child process has no knowledge about existence of its siblings, but its parent process has. If not explicitly required, you can let the parent to do so, i.e. to "end" all child processes.
BTW, instead of sending signal SIGUSR1 it is better to send SIGTERM signal. Although SIGUSSR1 can cause the target process be terminated by default (see signal(7)).
To "finish", i.e. to kill (or terminate) all the child processes as well as parent process, you can simplly kill the parent. All its descendants got killed as well. Or, you can send signal to the same process group. See kill(2).
You are declaring a pointer buf, but did not initialize it. Subsequent calls to read() and scanf() will fail because the pointer is invalid.
You need to make sure buf is initialized and pointing to valid memory. A simple way to fix your code is to do:
char buf[10];
read(fd[0], buf, 10);
If you enable compiler warnings with -Wall, then the compiler will warn you about initialized variables.
Be aware of potential buffer overflows: if you declare char buf[10], make sure you will never write more than ten bytes into it. Also, check the return value of functions like read(), write(), scanf() to ensure no errors were encountered, otherwise the contents of the buffers or output files might not be as expected.

creating pipe between father and child process

I'm trying to create a pipe between father and child process.
in this pipe, the child process will write data and the father will read and print it.
I don't know why but if I enter a big string the data got wrong, for strings with +- 7 words it still do fine.
I guess it is about the size of the buffer but can't fix it.
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* in this code i will make a child process with fork command
then i will create pipe using pipe commands.
i will transfer data from the child process to the father process
omriziner code
*/
void main(int argc,char *argv[])
{
if(argc < 2){
printf("prototype error \n<Enter any data you wana write> \n");
return;
}
int fd[2]; // creating array with 2 places for 2 fd'stdio
// fd[0] is set to read file in the pipe
//fd[1] is set to write file in the pipe
int piperes;
pid_t childpid;
char buff[5];
char * data = "learning to the exam";
printf("father pid %d:\n",getpid());
printf ("size of data is %d \n",(int)sizeof(argv[1]));
printf ("size of buff is %d \n",(int)sizeof(buff));
piperes = pipe(fd);
if(piperes < 0){
perror("PIPE ERR");
exit(1);
}
printf("Pipe succeed \n");
if((childpid = fork()) == -1){ // fork will create a child process
perror("FORK ERR");
exit(1);
}
// when fork suceed - the pid of the child will return in the parent and 0 will return in the child
// when fork fail - the pid will be -1
printf("Fork succeed, fork return is %d and process pid is %d :\n",childpid,getpid());
if(childpid == 0){ // if pid zero , wer in the child prcs
close(fd[0]);
write(fd[1],argv[1],sizeof(argv[1])); // send data to the write fd of the pipe
printf("data was written to fd[1] by pid : %d \n",getpid());
exit(0);
}
else{ // in this case, we're in the father process
close(fd[1]);
read(fd[0],buff,sizeof(argv[1])+1);
printf("Recived data is ''%s''", buff);
printf("By pid : %d \n",getpid());
exit(1);
}
}
sizeof(argv[1])
This does not do what you think it does.
sizeof is evaluated at compile-time1, and in this case will return 8 (assuming you're on a 64-bit machine), because argv[1] is a pointer.
Because you want the length of the string (which can only be known at run-time), you should instead use:
strlen(argv[1])
1 - There are cases where sizeof is evaluated at run-time. This is not one of them.

Working with pipes in Unix C

I am having serious trouble working with pipes in C. I'm supposed to take in arguments from the command line (example: ./myprogram 123 45 67), read the arguments one character at a time into a buffer, send the character to the child process to be counted, and then return the total number of characters read to the parent process. My code is as follows(note: the comments are what I'm supposed to be doing):
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
// Child process counts number of characters sent through pipe.
// Child process returns number of characters counted to parent process.
// Parent process prints number of characters counted by child process.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
static int toChild[2];
static int fromChild[2];
static char buffer;
int main(int argc, char **argv)
{
int status;
int nChars = 0;
pid_t pid;
pipe(toChild);
pipe(fromChild);
if ((pid = fork()) == -1) {
printf("fork error %d\n", pid);
return -1;
}
else if (pid == 0) {
close(toChild[1]);
close(fromChild[0]);
// Receive characters from parent process via pipe
// one at a time, and count them.
int count = 0;
printf("child about to read\n");
while(read(toChild[0], &buffer, 1)){
count++;
}
// Return number of characters counted to parent process.
write(fromChild[1], &count, sizeof(count));
close(toChild[0]);
close(fromChild[1]);
printf("child exits\n");
}
else {
close(toChild[0]);
close(fromChild[1]);
// -- running in parent process --
printf("CS201 - Assignment 3 - Chris Gavette\n");
write(toChild[1], &argv[1], 1);
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
read(fromChild[0], &nChars, 1);
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
close(toChild[1]);
close(fromChild[0]);
waitpid(pid, &status, 0);
printf("child counted %d chars\n", nChars);
printf("parent exits\n");
return 0;
}
}
The child process seems to hang even though I've closed both ends of both pipes.
For starters, this is wrong.
write(toChild[1], &count, 1)
It will eventually contribute to your problem. count is a int, not char or unsigned char. You need to send sizeof(count). Also, the read-function upon hitting an error will return EOF, which is non-zero, so your child exit condition is not appropriate. it should look something like this:
while(read(toChild[0], &buffer, 1) == 1)
Finally, your parent process should cycle through each argument in argv[] sending each as a strlen sized buffer.
I'm nearly certain this is what you're trying to do. Note that in order to maintain sanity in knowing which descriptor is used for a specific purpose, I prefer using a #define to note what each process uses for reading and writing. This can be extended to any number of processes, btw, which I'm sure is not too far down the line for your next assignment:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
// P0_READ - parent read source
// P0_WRITE - parent write target
// P1_READ - child read source
// P1_WRITE - child write target
#define P0_READ 0
#define P1_WRITE 1
#define P1_READ 2
#define P0_WRITE 3
#define N_PIPES 4
int main(int argc, char **argv)
{
int fd[N_PIPES], count = 0, i;
pid_t pid;
char c;
if (pipe(fd) || pipe(fd+2))
{
perror("Failed to open pipe(s)");
return EXIT_FAILURE;
}
// fork child process
if ((pid = fork()) == -1)
{
perror("Failed to fork child process");
return EXIT_FAILURE;
}
// child process
if (pid == 0)
{
// close non P1 descriptors
close(fd[P0_READ]);
close(fd[P0_WRITE]);
// get chars from input pipe, counting each one.
while(read(fd[P1_READ], &c, 1) == 1)
count++;
printf("Child: count = %d\n", count);
write(fd[P1_WRITE], &count, sizeof(count));
// close remaining descriptors
close(fd[P1_READ]);
close(fd[P1_WRITE]);
return EXIT_SUCCESS;
}
// parent process. start by closing unused descriptors
close(fd[P1_READ]);
close(fd[P1_WRITE]);
// send each arg
for (i=1; i<argc; ++i)
write(fd[P0_WRITE], argv[i], strlen(argv[i]));
// finished sending args
close(fd[P0_WRITE]);
// Wait for child process to return.
wait(NULL);
// wait for total count
if (read(fd[P0_READ], &count, sizeof(count)) == sizeof(count))
printf("Parent: count = %d\n", count);
// close last descriptor
close(fd[P0_READ]);
return 0;
}
Input
./progname argOne argTwo
Output
Child: count = 12
Parent: count = 12
Edit: Single Pipe with Child Return Status
It seems from the comments of the original question your assignment may call for reaping the return status of the child process as the result count rather than returning it in a pipe. In doing so, you can do this with a single pipe-descriptor pair. I prefer the first method, but this works as well:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
// P0_WRITE - parent write target
// P1_READ - child read source
#define P1_READ 0
#define P0_WRITE 1
#define N_PIPES 2
int main(int argc, char **argv)
{
int fd[N_PIPES], count = 0;
pid_t pid;
char c;
if (pipe(fd))
{
perror("Failed to open pipe(s)");
return EXIT_FAILURE;
}
// fork child process
pid = fork();
if (pid == -1)
{
perror("Failed to fork child process");
return EXIT_FAILURE;
}
if (pid == 0)
{
// close non P1 descriptors
close(fd[P0_WRITE]);
// Return number of characters counted to parent process.
while(read(fd[P1_READ], &c, 1) == 1)
++count;
close(fd[P1_READ]);
printf("Child: count = %d\n", count);
return count;
}
// parent process. start by closing unused descriptors
close(fd[P1_READ]);
// eacn each arg entirely
for (int i=1; i<argc; ++i)
write(fd[P0_WRITE], argv[i], strlen(argv[i]));
// finished sending args
close(fd[P0_WRITE]);
// Wait for child process to return.
if (wait(&count) == -1)
{
perror("Failed to wait for child process");
return EXIT_FAILURE;
}
printf("Parent: count = %d\n", WEXITSTATUS(count));
return 0;
}
The results are the same, but note this is a biach to to debug as most debuggers will signal-trip on your child process and the real exit status is lost. On my Mac, for example, running this under Xcode trips:
Failed to wait for child process: Interrupted system call
while running from the command line gives:
Child: count = 12
Parent: count = 12
One of the many reasons I prefer the two-pipe methodology.

Sending multiple strings using pipes to child process

I have a task in Linux and I can't get it work.
I have a program that receives a text file as parameter. It then creates a child process using fork() and sends to the child process, line by line the content of the text file received as parameter. The child process needs to count the lines and return to the parent process the number of lines received.
This is what I have until now, but somewhat the child process does not receive all the lines. For my test I used a text file with 9 lines. The parent sent 9 lines as strings but the child process received only 2 or 3 of them.
What am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char string[80];
char readbuffer[80];
int pid, p[2];
FILE *fp;
int i=0;
if(argc != 2)
{
printf("Syntax: %s [file_name]\n", argv[0]);
return 0;
}
fp = fopen(argv[1], "r");
if(!fp)
{
printf("Error: File '%s' does not exist.\n", argv[1]);
return 0;
}
if(pipe(p) == -1)
{
printf("Error: Creating pipe failed.\n");
exit(0);
}
// creates the child process
if((pid=fork()) == -1)
{
printf("Error: Child process could not be created.\n");
exit(0);
}
/* Main process */
if (pid)
{
// close the read
close(p[0]);
while(fgets(string,sizeof(string),fp) != NULL)
{
write(p[1], string, (strlen(string)+1));
printf("%s\n",string);
}
// close the write
close(p[1]);
wait(0);
}
// child process
else
{
// close the write
close(p[1]);
while(read(p[0],readbuffer, sizeof(readbuffer)) != 0)
{
printf("Received string: %s\n", readbuffer);
}
// close the read
close(p[0]);
}
fclose(fp);
}
A pipe is a unidirectional interprocess communication channel. You have to create 2 pipes, one to speak to the child process, the other to read data back.
Remember to close the unused side of the pipe on both processes.
You are sending the null terminator to the other process:
write(p[1], string, (strlen(string)+1));
That makes the result confusing because when you print what you've received, you only see up to the null terminator.
If you do this instead:
write(p[1], string, strlen(string));
you should get what you expect.
You're not counting the number of lines, you're counting the number of times read(2) returns.
When using pipes, read(2) will pull as much data as possible from the pipe: min(pipe_available, space_available). It doesn't care for newlines, 0 bytes etc. Simple tricks to make it work:
Use a loop to walk readbuffer and look for \n
Use fdopen + fgets (I have a feeling this is probably flawed)
look into manpage of pipe ( man 2 pipe ), the program you're trying to write is as an example there, compare it with yours :)

Resources