pipe not readable c - c

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.

Related

Comunication to from child process hanging on read in C

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);
}

How to prevent my client from hanging when exit is typed?

I am trying to implement a telnet like application where the user can connect to someone else's machine remotely. I have both server and client codes, but I feel like my client code is where I am wrong at. When user types "exit" I want my client to exit and get back to my working directory, but it doesn't. It hangs indefinitely. So I am posting only some code instead of posting the entire thing and also I have removed some error checking just for the sake of question here.
pid_t pid = fork();
switch(pid)
{
case -1:
error("Client: Error in forking.\n");
break;
case 0: // We are in the child process
{
char buffer[BUFFER_SIZE];
int nread;
// Read from the terminal/stdin and write to the socket
while((nread = read(STDIN_FILENO, buffer, BUFFER_SIZE)) > 0)
{
int result = write(sockfd, buffer, nread);
if(result == -1)
{
error("Error while writing to socket\n");
}
} // end while
} // end child
} // end switch
// Parent Read from the socket and write to the terminal/stdout
int nRead;
char buffer[BUFFER_SIZE];
while((nRead = read(sockfd, buffer, 512)) > 0)
{
int result = write(STDOUT_FILENO, buffer, nRead);
if(result == -1)
{
error("Client: Error while writing to stdout from socket.\n");
}
} // end while
kill(pid, SIGKILL);
close(sockfd);
// Wait for child to end before the parent
wait(NULL);
So basically after forking,
My child process will do this : read from terminal/stdin and write to the socket.
My parent will do this: read from the socket and write to the terminal or stdout.
The problem is that when I type exit, I want the child process to die since there is nothing else to write to the socket(since I want to close the connection with the server), but the kill statement never gets executed. I put a printf before to check that. I feel that the while loop before kill statement is infinite, if so, what to do to stop it.

linux read sys call doesnt get EOF

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.

Child process exit code != 0 when using pipes

int cpipe[2];
if(-1==pipe(cpipe)) {
printf("Pipe failed");
exit(1);
}
// Fork printenv
childPID=fork();
if(-1==childPID) {
printf("Fork failed");
exit(1);
}
else if (0==childPID) {
// if(-1==dup2(cpipe[1],1)); {
// printf("Pipe failed");
// exit(1);
// }
// close(cpipe[0]);
// close(cpipe[1]);
execl("/usr/bin/printenv","printenv",0);
printf("Execl failed");
_exit(1);
}
wait(&childExitStatus);
printf("\n%d \n",childExitStatus);
if(childExitStatus!=0) {exit(1);}
The above code works; gives childExitStatus = 0, and everyone is happy. Since i want the output of printenv to write to a pipe instead of being printed on screen, i put in the currently commented section. Decommenting that section, however gives the exit code 256, and everyone gets sad. What did i do wrong?
Your line:
if(-1==dup2(cpipe[1],1)); {
has a misplaced semicolon. Change the above line to:
if(-1==dup2(cpipe[1],1)){
and you should see the correct return value.
exit(1) was always getting called inside the if statement because the if statement was ended with the semicolon and the code inside the braces was always getting executed.
I managed to make it work by using dup instead of dup2 in the child process. The original code always failed at dup2 (I changed the exit status for each specific error). But I don't know why.
} else if (childPID == 0) {
close(1);
dup(cpipe[1]);
...
}
I also added reading from cpipe[0] in the parent process, just to test:
char c[4096];
write(1, c, read(cpipe[0], c, 4096));
close(cpipe[0]);
int childExitStatus;
wait(&childExitStatus);
printf("\n%d \n", childExitStatus);
if (childExitStatus != 0)
_exit(5);
Try not write:
close(cpipe[1]);
because printenv closes the cpipe[1]. If I am not mistaken.

Multiprocess, Forks and Pipes in C

I posted a question previously about using fork() and pipes in C
. I changed the design a little bit so that it reads a regular txt file and sorts the words in the file. So far, this is what I came up with:
for (i = 0; i < numberOfProcesses; ++i) {
// Create the pipe
if (pipe(fd[i]) < 0) {
perror("pipe error");
exit(1);
}
// fork the child
pids[i] = fork();
if (pids[i] < 0) {
perror("fork error");
} else if (pids[i] > 0) {
// Close reading end in parent
close(fd[i][0]);
} else {
// Close writing end in the child
close(fd[i][1]);
int k = 0;
char word[30];
// Read the word from the pipe
read(fd[i][0], word, sizeof(word));
printf("[%s]", word); <---- **This is for debugging purpose**
// TODO: Sort the lists
}
}
// Open the file, and feed the words to the processes
file_to_read = fopen(fileName, "rd");
char read_word[30];
child = 0;
while( !feof(file_to_read) ){
// Read each word and send it to the child
fscanf(file_to_read," %s",read_word);
write(fd[child][1], read_word, strlen(read_word));
++child;
if(child >= numberOfProcesses){
child = 0;
}
}
where numberOfProcesses is a command-line argument. So what it does is that it reads each word in the file and send it to a process. This however, does not work. When I print the word in the child process, it doesn't give me the correct output. Am I writing/reading the words correctly to/from the pipes?
Are the words being printed in the wrong order or interleaved? The thing is that when you write a word to a pipe, you are expecting the process handling that pipe to be scheduled immediately and to print the word. Then you expect the main process to run again and the next word to be written to the next pipe etc.
But that is not guaranteed to happen. Your main loop might write all of the words to all of the pipes before any of the other processes is scheduled. Those processes might not be scheduled in the order you expect. And the printf calls might interfere with each other so that their output becomes interleaved.
If you really want to do what you set out to, then Posix threads would be better. If you just wanted to learn something about using multiple processes, then I guess you have :-)
In the parent, you write strlen() bytes, which might be less than 30 bytes. In the child, however, you always try to read 30 bytes. You also must NUL-terminate the word, otherwise you might see garbage or a runaway string in your printf() statement.
In the child, you must either parse and split the input at word boundary or use stdio as #JonathanLeffler proposed. When you use stdio, you get all this buffering and word reading for free.
int n;
char word[31];
/* Read the word from the pipe */
n = read(fd[i][0], word, sizeof(word) - 1);
if (n == -1) {
perror("read");
/* do error handling */
} else {
word[n] = 0;
printf("[%s]", word);
}

Resources