Reputation: 33
I'm trying to read a text file's string from a process, then deliver the string to another process via named pipes on LINUX. The problem is when i type './reader text.txt = receiver' to the console the recieving process' read() function returns an error if i put the line
fcntl(fd, F_SETFL, O_NONBLOCK);
or gets stuck on read() function if i remove it.
heres the process that reads the string (reader)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc,char *argv1[]){
if(argc==4 && strcmp(argv1[2],"=") == 0){
char mesaj[99999]; //message to be delivered
char line[150];
FILE *fp =fopen(argv1[1],"r"); //reading from a text file
if(fp==NULL){
printf("text file error!");
exit(1);
}
while(fgets(line,sizeof(line),fp)){
strcat(mesaj,line); //append every line to message
}
fclose(fp);
mesaj[strlen(mesaj)-1]='\0';
int n =strlen(mesaj)+1;
//printf("got the text %s\n",mesaj);
if(mkfifo("myFifo",0777)== -1 && errno!= EEXIST){
printf("Pipe error");
exit(1);
}
printf("opening\n");
int fd= open("myFifo",O_RDWR);
if(fd==-1){
printf("open error");
exit(1);
}
printf("opened");
if( write(fd, mesaj,sizeof(char)*n)==-1){
printf("write error");
exit(1);
}
printf("written");
close(fd);
printf("closed");
fflush(stdout);
char mesajSizeChar[n];
sprintf(mesajSizeChar, "%d", n);
char *args[]={mesajSizeChar,NULL}; //send the message size as parameter for the other process
char program[]="./";
strcat(program,argv1[3]); // recieved process name as parameter
execv(program,args); // call the other process
perror("execv");
return 0;
}
}
and heres the recieving process (reciever)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
int main(int argc,char *argv1[]){
int mesajSize=atoi(argv1[0]); //convert message size to integer
char mesaj[99999];
printf("\ncame here\n");
int fd= open("myFifo",O_RDWR);
fcntl(fd, F_SETFL, O_NONBLOCK);
printf("\nopen \n");
if(fd==-1)
printf("pipe error\n");
if(read(fd,mesaj,sizeof(char)*mesajSize)==-1)
printf("read error\n");
printf("read \n");
printf("\nworked: %s \n",mesaj);
close(fd);
return 0;
}
Upvotes: 2
Views: 1624
Reputation: 180058
To use a FIFO or pipe, sender and receiver must run concurrently, but you are trying to run them sequentially. A FIFO or pipe has no persistent storage, so the system does not allow you to write to one unless unless at least one process has the read end open, so as to be able to read it.
Ordinarily, attempts to open a FIFO for writing will block while there are no readers, and vice versa. Your reader is working around this by opening the FIFO for both reading and writing, even though it intends only to write. You will find that if it tries to send too much data to the FIFO then it blocks, because nothing is reading the data, and pipes / FIFOs have limited buffer capacity. When it closes the FIFO's fd, leaving no process with it open, all data previously written to it are lost.
Your receiver also erroneously opens the FIFO for both reading and writing, whereas it should open it only for reading. There being no data to read from it, I would expect attempts to read from it to block indefinitely, unless you put it into non-blocking mode. This seems to be exactly what you describe.
To fix it, I would suggest
taking the code that starts the receiver out of the reader. Instead, start the reader and receiver separately. Alternatively, the reader may start out by fork()
ing, with the resulting child process execv()
ing the receiver.
The reader should open the FIFO with flag O_WRONLY
, and the receiver should open it with mode O_RDONLY
.
You should find a different way to convey the message length from reader to receiver, or, better, to avoid needing to tell it the message length in advance at all. You could, for instance, send an initial fixed-length message that conveys the length of the main message data, but more typical would be for the receiver to just keep reading data until it sees EOF.
The reader will cause the receiver to see EOF on the FIFO by closing it, either explicitly or by terminating. This depends on the receiver having it open in read-only mode, however, and there being no other writers.
The reader probably should not attempt to buffer the whole message in memory at once. It should not, in any case, assume that a write()
call will transfer the full number of bytes requested -- the return value will tell you how many actually were transferred. You need to be prepared to use multiple write()
calls in a loop to transfer all the data.
Similarly, the receiver cannot rely on a single read()
call to transfer the full number of bytes requested in one call, even if it has some way to know how many are coming. As with write()
, you need to be prepared to use multiple read()
s to transfer all the data.
Upvotes: 3
Reputation: 780688
The problem is that you closed the pipe in the first process. A pipe doesn't have any permanent storage, it can only hold data while it's open by at least one process. When you close the pipe, the data that you've written to it is discarded.
As a result, when the second process tries to read from the pipe, there's nothing available.
You need to keep the pipe FD open when you execute the second process. Get rid of close(fd);
in the reader program.
Upvotes: 3