First of all sorry if my English won't be fluent and clear.
I'm working on understanding pipes and communication between processes. I have tried to implement two c programs, the one writes into a certain pipe from what he reads from the standard input and the other one waits until the pipe opens and reads from it and prints to the standard output until EOF.
Here is the code for the writer pipe:
fd = open(filename, O_RDWR);
if(fd == -1) print_error();
while(fgets(buffer, BUFFER_SIZE, stdin) != NULL) {
if(write(fd, buffer, BUFFER_SIZE) == -1) print_error();
}
and here is the code for the reader pipe:
while(1) {
if((fd = open(filename, O_RDWR)) == -1) {
if(errno == ENOENT) sleep(1);
else print_error();
}
else {
while(read(fd, buffer, BUFFER_SIZE) != 0) {
fprintf(stdout, "%s", buffer);
}
}
}
The thing is that when I run those two programs concurrently the basic concept works, I write something to the standard input in the writer program and I see in the other terminal that the reader program prints it to the standard output. The problem is that when I send EOF by hitting CTRL + D for the writer program the reader program still waits for input, and I know for sure that it isn't because the while(1), I saw in the debugger that the read syscall is just waiting to input and didn't understand that we got EOF, the line : read(fd, buffer, BUFFER_SIZE) didn't evaluate even though there is no input.
I hope that I gave all the data needed to solve the problem, anyone have any ideas what gone wrong?
If fgets() detects EOF it returns either what already is in its read buffer or just returns NULL.
In the latter case you want to notify the reading end that the transmission is over by for example just closing the pipe.
int fd = open(filename, O_RDWR);
if (fd == -1)
{
perror("open() failed");
}
else
{
while (fgets(buffer, BUFFER_SIZE, stdin) != NULL)
{
if (write(fd, buffer, BUFFER_SIZE) == -1)
{
perror("write() failed");
break;
}
}
if (ferror(stdin))
{
perror("fgets() failed");
}
close(fd); /* The other end's blocking call to read() would return 0. */
}
In no case something like EOF is read. EOF is not a character but a state.
From man 3 read:
If some process has the pipe open for writing [...] read() shall block the calling thread until some data is written or the pipe is closed by all processes that had the pipe
open for writing.
Also this code does not cover read() failing:
while(read(fd, buffer, BUFFER_SIZE) != 0) {
fprintf(stdout, "%s", buffer);
}
It should look for example like this:
ssize_t result;
while (0 != (result = read(fd, buffer, BUFFER_SIZE)))
{
if (0 > result)
{
if ((EINTR == errno) || (EAGAIN == errno))
{
continue;
}
perror("read() failed");
break;
}
fprintf(stdout, "%s", buffer);
}
Even more this loop ...
while(1) {
if((fd = open(filename, O_RDWR)) == -1) {
if(errno == ENOENT) sleep(1);
else print_error();
}
...
}
... misses to close() fd, before (re-)opening it.
If the reader does not write let it open the pipe by specifying O_RDONLY instead of RDWR.
read can also return -1 on error. This might be the case if your writer process doesn't close his output stream properly. So you should definitively check for read <= 0.
But I hope that you are also aware that your surrounding while(1) loop will make it immediately enter the read call for a next round, so it might also be that you see just that.
Related
I'm trying to communicate with an external program which, if executed, will run a terminal interface.
Normally I'll have to provide some inputs (e.g. "1+1") and then read the output of the program (e.g. "2").
Since I need a two-way communication I wasn't able to use popen().
My problem is the following:
Whenever I have a part of the code that asks for inputs, for example containing std::cin >> input I run into the same issue, the read command never exits.
Here I wrote a minimal example, all the child process does is reading the input and repeating it.
When I try to run this code what happens is that I see the first print "Parent says:" and I can provide the input and send it using write. However, when I try to call again the read() function the see the outcome, it never exit.
I've noticed that if I close the pipe that goes from the parent to the child (fd_p2c[1]), then I can read successfully.
This is clearly not what I want, since in my application I'd like to keep both communications open.
Any suggestions on what could be done to fix this problem?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
int status, buf_length;
// Input and output
char buf[256];
char msg[256];
char child_read[256];
int fd_c2p[2]; // file descriptor pipe child -> parent
int fd_p2c[2]; // file descriptor pipe parent -> child
pipe(fd_c2p);
pipe(fd_p2c);
// Spawn a new process with pid
pid_t pid = fork(); // Fork process
if (pid == 0) {
// Child
// Close the unused end of the pipe
if (close(fd_p2c[1]) != 0 || close(fd_c2p[0]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// Set the comunication
if (dup2(fd_p2c[0], STDIN_FILENO) != 0 ||
dup2(fd_c2p[1], STDOUT_FILENO) != 1 ||
dup2(fd_c2p[1], STDERR_FILENO) != 2) {
fprintf(stderr, "Faild to duplicate the end of the pipes\n");
exit(1);
}
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// ask kernel to deliver SIGTERM in case the parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM);
// Moch program
while (1) {
fprintf(stdout, "Parent says: ");
fflush(stdout);
scanf("%s", child_read);
fprintf(stdout, " >> Child repeat: %s\n", child_read);
fflush(stdout);
}
exit(1);
} else {
// Parent
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
}
// Read output and send input
while (1) {
// Read from child
while (buf_length = read(fd_c2p[0], buf, sizeof(buf) - 1)) {
buf[buf_length] = '\0';
printf("%s", buf);
}
// Enter message to send
scanf("%s", msg);
if (strcmp(msg, "exit") == 0)
break;
// Send to child
write(fd_p2c[1], msg, strlen(msg));
//close(fd_p2c[1]);
}
printf("KILL");
kill(pid, SIGKILL); // send SIGKILL signal to the child process
waitpid(pid, &status, 0);
}
One problem is in the child-process with:
scanf("%s", child_read);
With the %s format there's only three things that will stop scanf from waiting for more input:
Error
End of file
Space
Assuming nothing goes wrong, there will be no errors. And since the parent process keeps the pipe open there will be no end of file. And since the parent process writes only what it itself reads with scanf("%s", ...) there will be no spaces in the data sent.
All in all, the child process will wait indefinitely for scanf to return, which it never will.
Let's call a word what scanf("%s") is able to extract.
This is a contiguous sequence of characters that are not separators (space, tab, new-line...).
The (redirected) standard input of the child reads a word with scanf("%s", child_read);.
This word is known as ended when a separator is read or EOF is reached.
In the parent, write(fd_p2c[1], msg, strlen(msg)); sends a word (and nothing more right after it) because msg is extracted just before as a word.
Note that when you input a word with the keyboard, you also hit the enter key which sends the new-line separator in the standard input. At this time, the terminal makes this line available to scanf(), the word is known as ended and the separator is ignored (in this specific situation, but we could obtain it with fgetc()).
For example, if in the standard input of the parent we input "abc\n",
the parent obtains the word "abc" which is sent as is to the child.
Then the child receives a word starting with "abc" but not ended yet: scanf("%s") is still waiting some other characters after c to make this word longer or a separator or EOF to detect the end of this word.
You can for example send a separator after this word.
// Send to child
write(fd_p2c[1], msg, strlen(msg));
char lf='\n';
write(fd_p2c[1], &lf, 1);
Or maybe it's better to rely on fgets() (instead of scanf("%s")) to obtain a line (not just a word) both in the child and the parent.
By the way, the while/read in the parent looks weird to me.
I would do something like this.
// Read from child
buf_length = (int)read(fd_c2p[0], buf, sizeof(buf) - 1);
if (buf_length <= 0) {
break;
}
If you need a two-ways communication, you could use some unix(7) socket, or several pipe(7)-s, or some fifo(7).
You could use some JSONRPC library. Or XDR (perhaps ONC/RPC/XDR) or ASN/1 for binary communication between heterogeneous computers in a data center, or MPI. If you can use some supercomputer, it probably has proprietary libraries to ease message passing between processes running on different nodes.
Consider using OpenMPI.
I'm trying to communicate with an external program which, if executed, will run a terminal interface.
Maybe you then need some pty(7) with termios(3) ? Then take inspiration from the source code of xterm or of rxvt
You probably need some event loop around a poll(2) before attempting a read(2) (or recv(2)...) or a write(2) (or send(2))
You could find open source libraries (e.g. Glib, libev, ...) to help you, and you certainly should study for inspiration their source code.
Since in general, I wouldn't know the length of the message in that I need to read form fd_c2p, I need to create a look that listens to the pipe till its empty.
To do it is necessary to add the O_NONBLOCK to the file descriptor in the parent as suggested by #some-programmer-dude:
// Parent
// close unused pipe ends
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// Add O_NONBLOCK the the fd that reads from the child
int c2p_flags = fcntl(fd_c2p[0], F_GETFL);
fcntl(fd_c2p[0], F_SETFL, c2p_flags | O_NONBLOCK);
Now, when I read the output of the child from the file descriptor fd_c2p[0], it returns an error whenever we attempt to read from an empty file.
Reading the error code in errno should match EWOULDBLOCK.
To know when to stop to read from fd_c2p[0] some knowledge on the output is needed.
This particular reading should stop when the last character of the line when it reaches EWOULDBLOCK and the previous message ended with :.
// Read from child
end_of_message = false;
while (1) {
buf_length = read(fd_c2p[0], buf, sizeof(buf) - 1);
if (buf_length == -1)
{
if (end_of_message && errno == EWOULDBLOCK)
break;
else if (errno == EWOULDBLOCK)
continue;
else {
fprintf(stderr, "reading from pd_c2p returned an error different "
"from `EWOULDBLOCK'\n");
exit(errno);
}
}
buf[buf_length] = '\0';
printf("%s", buf);
end_of_message = buf[buf_length - 1] == ':';
}
This patch solves the problem of reading from the file when one is not sure about how many lines are there before the program asks for inputs.
It should also be safe when the message contains : at any position. To test it, one can reduce the different buffers to a smaller size (e.g. from 256 to 1).
It was also pointed out from by #prog-fh, that in principle one would like to have inputs that contains spaces as well. To accomodate can use fgets instead of scanf:
// Enter message and send it over to the chid process
while (fgets(msg, 256, stdin) != NULL) {
if (msg[strlen(msg)] == '\0')
write(fd_p2c[1], msg, strlen(msg));
else {
fprintf(stderr, "Error encounter while reading input\n");
exit(1);
}
if (msg[strlen(msg) - 1] == '\n')
break;
else
continue;
}
Among the advantages of using fgets there is the fact that the string will keep the newline \n at the end, meaning that there is no need to push an extra character to the write buffer once we are done reading the message.
The complete code is then
#include <cerrno>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
int status, buf_length;
bool end_of_message = false;
int fd_c2p[2]; // file descriptor pipe child -> parent
int fd_p2c[2]; // file descriptor pipe parent -> child
// Input and output
char buf[256];
char msg[256];
char child_read[256];
// We need two pipes if we want a two way comunication.
pipe(fd_c2p);
pipe(fd_p2c);
// Spawn a new process with pid
pid_t pid = fork(); // Fork process
if (pid == 0) {
// Child
// Close the unused end of the pipe
if (close(fd_p2c[1]) != 0 || close(fd_c2p[0]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// Set the comunications
if (dup2(fd_p2c[0], STDIN_FILENO) != 0 ||
dup2(fd_c2p[1], STDOUT_FILENO) != 1 ||
dup2(fd_c2p[1], STDERR_FILENO) != 2) {
fprintf(stderr, "Faild to duplicate the end of the pipes\n");
exit(1);
}
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// ask kernel to deliver SIGTERM in case the parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM);
// Moch Program
while (1) {
fprintf(stdout, "Parent says:");
fflush(stdout);
fgets(child_read, 256, stdin);
fprintf(stdout, " >> Child repeat: %s", child_read);
while (child_read[strlen(child_read) - 1] != '\n') {
fgets(child_read, 256, stdin);
fprintf(stdout, " >> Child repeat: %s", child_read);
}
fflush(stdout);
}
// Nothing below this line should be executed by child process.
// If so, it means that thera has beed a problem so lets exit:
exit(1);
} else {
// Parent
// close unused pipe ends
// These two pipe ends are not needed anymore
if (close(fd_p2c[0]) != 0 || close(fd_c2p[1]) != 0) {
fprintf(stderr, "Faild to close unused end of pipe\n");
exit(1);
}
// Add O_NONBLOCK the the fd that reads from the child
int c2p_flags = fcntl(fd_c2p[0], F_GETFL);
fcntl(fd_c2p[0], F_SETFL, c2p_flags | O_NONBLOCK);
}
// Now, you can write to fd_p2c[1] and read from fd_c2p[0] :
while (1) {
// Read from child
end_of_message = false;
while (1) {
buf_length = read(fd_c2p[0], buf, sizeof(buf) - 1);
if (buf_length == -1)
{
if (end_of_message && errno == EWOULDBLOCK)
break;
else if (errno == EWOULDBLOCK)
continue;
else {
fprintf(stderr, "reading from pd_c2p returned an error different "
"from `EWOULDBLOCK'\n");
exit(errno);
}
}
buf[buf_length] = '\0';
printf("%s", buf);
end_of_message = buf[buf_length - 1] == ':';
}
// Enter message and send it over to the chid process
while (fgets(msg, 256, stdin) != NULL) {
if (msg[strlen(msg)] == '\0')
write(fd_p2c[1], msg, strlen(msg));
else {
fprintf(stderr, "Error encounter while reading input\n");
exit(1);
}
if (msg[strlen(msg) - 1] == '\n')
break;
else
continue;
}
// Check if the user wants to exit the program
if (strcmp(msg, "exit\n") == 0)
break;
}
printf("KILL");
kill(pid, SIGKILL); // send SIGKILL signal to the child process
waitpid(pid, &status, 0);
}
I'm in the process of properly understanding pipes and FDs and I'm trying to program the following thing:
The program basically compresses stuff like gzip does with the options -cf.
The basic idea is:
I create two pipes in the parent process, then I fork it twice so that I'll have two children. In the first child, I redirect the first pipe's read end to stdin, and the second pipe's write end to stdout. Then I exec gzip with the -cf options so that it'll write to stdout (now the writing end of pipe2)
In the second child, I read from pipe2 and either output it directly or save it to a file.
The problem is, however, that no data arrives at the second child and I'm not really sure why. Here's the code:
int main(int argc, char **argv) {
char *file;
int out = 0;
if(argc == 2) {
file = argv[1];
out = 1;
} else if (argc > 2) {
exit(EXIT_FAILURE);
}
int c1pipe[2];
int c2pipe[2];
pipe(c1pipe);
pipe(c2pipe);
int f;
for(int i = 0; i < 2; i++) {
switch(f = fork()) {
case 0: //child
if(i == 0) { //first loop iteration, child 1
close(c1pipe[1]);
dup2(c1pipe[0], fileno(stdin));
close(c1pipe[0]);
close(c2pipe[0]);
dup2(c2pipe[1], fileno(stdout));
close(c2pipe[1]);
execlp("gzip", "gzip", "-cf", (char *) NULL);
} else if (i == 1) { //second loop iteration, child2
close(c1pipe[0]);
close(c1pipe[1]);
close(c2pipe[1]);
FILE *read = fdopen(c2pipe[0], "r");
char buffer[1024];
if(out == 0) { //output to stdout
while(fgets(buffer, 1024, read) != NULL) {
fprintf(stdout, "%s", buffer);
fflush(stdout);
}
} else { //write to specified file
FILE *writeto = fopen(file, "w");
while(fread(buffer, sizeof(char), strlen(buffer)+1, read) > 0) {
fwrite(buffer, sizeof(char), strlen(buffer)+1, writeto);
fflush(writeto);
}
fclose(writeto);
}
close(c2pipe[0]);
fclose(read);
}
break;
case -1: //err
//not implemented
break;
default: //parent
if(i == 0) {
close(c2pipe[0]);
close(c2pipe[1]);
close(c1pipe[0]);
FILE *writer;
writer = fdopen(c1pipe[1], "w");
char buffer[1024];
while(fgets(buffer, sizeof buffer, stdin) != NULL) {
fwrite(buffer, sizeof (char), strlen(buffer)+1, writer);
}
close(c1pipe[1]);
fclose(writer);
}
break;
}
}
return 0;
}
Please excuse the missing error handling as I wanted to create a quick-and-dirty version.
Any help is appreciated.
In the parent process, you are closing both ends of c2pipe before you have forked the second child.
You'd probably have figured this out already if you had put any error handling in on any of the read/write calls. In fact, if you checked for an error on the dup2 calls and then looked at errno, you probably would have found that it was EBADF (bad file descriptor).
Another issue is that your parent process exits before it knows that both child processes have finished. This means that the child processes will receive a signal and will themselves be terminated. The parent needs to call one of the variants of wait() to make sure both children have gone.
Parent has opened a file to read, I fork two children to read from file and write on different files.
child 1 reads the first line, and child 2,reads nothing. When I do an ftell, it reaches the end.
Can anyone please explain this behaviour?
f[0] = fopen("input", "r");
for ( i = 1; i <= 2; i++ ){
if ((pid = fork()) != 0){
waitpid(pid);
}
else
{
snprintf ( buffer, 10, "output%d", i );
printf("opening file %s \n",buffer);
f[i] = fopen( buffer, "w");
fgets(buff2, 10, f[0]);
fprintf(f[i], "%s", buff2);
fclose(f[i]);
_exit(0);
}
}
fclose(f[0]);
Your problem is buffering. stdio reads files on fully buffered mode by default, which means a call to fgets(3) will actually read a huge block of characters from the file, buffer everything, and then return the first line, while leaving the rest in the buffer, in the perspective of being called again in the future (remember that stdio strives for minimizing the number of read(2) and write(2) syscalls). Note that stdio buffering is a user-space thing; all the kernel sees is a single process reading a huge block on that file, and so the cursor is updated accordingly.
Common block sizes are 4096 and 8192; your input file is probably smaller than that and so the first process that calls fgets(3) ends up reading the whole file, leaving the cursor in the end. Buffering is tricky.
What can you do? One solution I can think of is to disable buffering (since this is an input stream we're talking about, we can't use line buffered mode, because line buffering is meaningless for input streams). So if you disable buffering on the input stream before forking, everything will work. This is done with setvbuf(3).
Here's a working example:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
static FILE *f[3];
static char buffer[128];
static char buff2[128];
int main(void) {
pid_t pid;
int i;
if ((f[0] = fopen("input", "r")) == NULL) {
perror("Error opening input file");
exit(EXIT_FAILURE);
}
if (setvbuf(f[0], NULL, _IONBF, 0) < 0) {
perror("setvbuf(3) failed");
exit(EXIT_FAILURE);
}
for (i = 1; i <= 2; i++) {
if ((pid = fork()) < 0) {
perror("fork(2) failed");
exit(EXIT_FAILURE);
}
if (pid != 0) {
if (waitpid(pid, NULL, 0) < 0) {
perror("waitpid(2) failed");
exit(EXIT_FAILURE);
}
} else {
snprintf(buffer, sizeof(buffer), "output%d", i);
printf("opening file %s\n", buffer);
if ((f[i] = fopen(buffer, "w")) == NULL) {
perror("fopen(2) failed");
exit(EXIT_FAILURE);
}
errno = 0;
if (fgets(buff2, sizeof(buff2), f[0]) == NULL) {
if (errno != 0) {
perror("fgets(3) error");
exit(EXIT_FAILURE);
}
}
fprintf(f[i], "%s", buff2);
fclose(f[i]);
exit(EXIT_SUCCESS);
}
}
fclose(f[0]);
return 0;
}
Note that this may incur a significant performance hit. Your code will be making a lot more syscalls, and it might be too expensive for huge files, but it doesn't seem to be a problem since apparently you're dealing with relatively small input files.
Here's an extract of my fork() man page:
The child process has its own copy of the parent's descriptors. These descriptors reference the same underlying objects, so that, for instance, file pointers in file objects are shared between the child and the parent, so that an lseek(2) on a descriptor in the child process can affect a subsequent read or write by the parent. This descriptor copying is also used by the shell to establish standard input and output for newly created processes as well as to set up pipes.
So your behaviour is completely normal. If you want your child to have its own file descriptor, it should open its own file.
For example, you could do the following:
for ( i = 1; i <= 2; i++ )
{
if ((pid = fork()) != 0)
{
waitpid(pid);
}
else
{
f[0] = fopen("input", "r"); // New
snprintf ( buffer, 10, "output%d", i );
printf("opening file %s \n",buffer);
f[i] = fopen( buffer, "w");
fgets(buff2, 10, f[0]);
fprintf(f[i], "%s", buff2);
fclose(f[i]);
fclose(f[0]); //New
_exit(0);
}
}
Also, you should check for errors (almost all the functions in your else could fail with error).
I need to write a program that create pipe send filename from command line to child process. In child read that file and send it back using pipe. Parent process should print the file. if error occur in child process error must be send to parent process.
here is my code, it print some junk along file file (and also it disable scrolling in terminal emulator when I run it).
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void main(int argc, char *argv[]) {
int pipefd[2];
char buff[100];
int childpid;
int size;
FILE *file;
if (argc != 2) {
printf("usage:\n%s <filename>\n", argv[0]);
exit(1);
}
if (pipe(pipefd) < 0) {
perror("can't open pipe\n");
}
if ((childpid = fork()) == 0) {
sleep(1);
size = read(pipefd[0], buff, sizeof(buff));
file = fopen(buff, "r");
if (file == NULL) {
write(pipefd[1], "Can't open file", 15);
exit(1);
}
while (!feof(file)) {
if (fgets(buff, sizeof(buff), file) == NULL) {
write(pipefd[1], "Error reading file", 18);
} else {
write(pipefd[1], buff, sizeof(buff));
}
}
} else if (childpid > 0) {
size = strlen(argv[1]);
if (write(pipefd[1], argv[1], size) != size) {
perror("Error writing to pipe\n");
}
wait(NULL);
while ((size = read(pipefd[0], buff, sizeof(buff))) > 0) {
write(1, buff, size);
}
}
exit(0);
}
Your program works as intended after quite a few changes. Lets list out what all changes are required and why-
I) Both in the child and parent, close the respective pipes as soon as you are done with them. From man page of read(3),
If some process has the pipe open for writing and O_NONBLOCK is clear,
read() shall block the calling thread until some data is written or
the pipe is closed by all processes that had the pipe open for
writing.
So do something like this in your code everywhere where the job pipes is over,
size = read(pipefd[0], buff, sizeof(buff));
close(pipefd[0]);
write(pipefd[1], buff, strlen(buff));
close(pipefd[1]);
if (write(pipefd[1], argv[1], size) != size) {
perror("Error writing to pipe\n");
}
close(pipefd[1]);
while ((size = read(pipefd[0], buff, sizeof(buff))) > 0)
{
write(1, buff, size);
}
close(pipefd[0]);
You hadn't closed the write end of of the pipe in the child and your parent was blocking in the read
II) You are using something like while(fgets(...)) in a loop to read data from file. This will bomb when there are newlines in the file and fgets returns multiple times, overwriting the buffer everytime during the process
I always use simple fgetc and feof combination to read from file. So, change your file reading mechanism to something like
unsigned count=0;
while (!feof(file) && count < sizeof(buff))
buff[count++]=fgetc(file);
if (feof(file))
buff[--count]=0;
else
buff[sizeof(buff)-1]=0;
III) While writing the file data from the child, you should use strlen(as we have already made sure buffer is null terminated, see above ) and not sizeof as the buffer may not be full at all and you will end up writing junk. So, change
write(pipefd[1], buff, sizeof(buff));
to
write(pipefd[1], buff, strlen(buff));
IV) Follow a safe exit from the child and parent after their job is done. Something like
close(pipefd[1]);
_exit(EXIT_SUCCESS); // in child
and
close(pipefd[0]);
exit(EXIT_SUCCESS); // in parent
PS: I've changed the file reading logic, so your compiler error is gone now and do follow the advice given by n.m.
You cannot write sizeof(buf) meaningful bytes if fgets returned less than that. The rest will be filled with junk.
Moreover, mixing string-oriented fgets with binary read/write is a bad style. Use read or fread to read the file. They return number of bytes read, use this number as an argument to write.
This code does not compile:
while (fgets(buff, sizeof(buff), file) != NULL) {
write(pipefd[1], "Error reading file", 18);
} else {
write(pipefd[1], buff, sizeof(buff));
}
You can't have an else clause there.
I'm working on some code involving pipes. The idea is that I'm supposed to have a process looping indefinitely and add data to the pipe as it comes (I'm testing this by reading a file, and going line by line in a while loop).
If I set the other process (the one that reads the pipe) to sleep so the entire file is read I have no problems and get all the file in the output. As soon as I remove the sleep (so now the 2 processes start simultaneously with the 2nd process reading the information off the pipe as it comes), my code goes straight to the else block of my code below and I never see any actual output. What am I doing wrong?
close(pipe[1]);
sleep(5);
while (1) {
nbytes = read(pipe[0], buffer, 200);
if(errno != EWOULDBLOCK) {
printf("%s", buffer);
}
else {
printf("I am not blocked here\n");
sleep(1);
}
}
Thanks
Two things:
did you make pipe[0] non-blocking? It'll be something like int nbio=1; ioctl(pipe[0], FIONBIO, &nbio);
you're checking for error wrong.
if(nbytes > 0) {
/* you may need to null-terminate the input buffer prior to display */
buffer[nbytes] = '\0';
printf("%s", buffer);
}
else if(errno == EWOULDBLOCK) {
printf("I am not blocked here\n");
sleep(1);
}
else {
printf("some other error occurred - if nbytes == 0, then it's EOF.\n");
}
probably errno is EWOULDBLOCK the first time through, and then doesn't get updated on successful read, so it looks like EWOULDBLOCK again.