Weird behaviour of fifos on linux - c

I'm studying linux fifos and I made two small C programs which communicate through fifo. The first one acts like a server, it receive a pattern and executes a command using that pattern. The second one acts like a client, it sends the pattern and receive the result. I want the server to be capable of serving multiple requests, not necessarily simultaneously, but the weird thing is that after the first client is served it just stops although I put there an infinite loop.
server.c
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
void siginthandler(int i){
remove("./fifo1");
remove("./fifo2");
printf("Got SIGINT signal\n");
exit(EXIT_SUCCESS);
}
int main(int argc, char *argv[]){
signal(SIGINT, siginthandler);
int f = mkfifo("./fifo1", 0600);
if (f == -1){
perror("Unable to create fifo1\n");
exit(EXIT_FAILURE);
}
f = mkfifo("./fifo2", 0600);
if (f == -1){
perror("Unable to create fifo2\n");
exit(EXIT_FAILURE);
}
int fd1 = open("./fifo1", O_RDONLY);
int fd2 = open("./fifo2", O_WRONLY);
if (fd1 == -1 || fd2 == -1){
perror("Unable to open fifos\n");
exit(EXIT_FAILURE);
}
while (1){
char buf[50];
char pattern[50];
read(fd1, pattern, 50);
char command[80] = "ps -e | grep ";
strcat(command, pattern);
FILE *result = popen(command, "r");
while (fgets(buf, 50, result)){
write(fd2, buf, 50);
//printf("%s", buf);
}
memset((void *) buf, 0, 50);
write(fd2, buf, 50);
pclose(result);
}
remove("./fifo1");
remove("./fifo2");
return 0;
}
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int fd1 = open("./fifo1", O_WRONLY);
int fd2 = open("./fifo2", O_RDONLY);
if ((fd1 == -1) || (fd2 == -1)){
perror("Unable to find fifos");
exit(EXIT_FAILURE);
}
char input[50];
printf("Give pattern: ");
scanf("%s", input);
write(fd1, input, 50);
char buf[50];
while (read(fd2, buf, 50) == 50){
if (buf[0] == 0){
break;
}
printf("%s", buf);
}
return 0;
}

When the first client closes the FIFO, the server gets EOF on the FIFO, and continues to get no new data in perpetuity. The server has to reopen the FIFO for the next client. If there were multiple clients all with the FIFO open concurrently, the server would not get EOF until the last of the clients disconnected (as long as there is one writer, the reader — the server — will be OK).
This is expected behaviour — or, since you weren't expecting it, is the behaviour that should be expected.
Of course, since your code completely ignores the return value from read(), you have no idea what, if anything, is being read.
The code:
memset((void *) buf, 0, 50);
write(fd2, buf, 50);
is curious; why would you send a buffer of 50 0 bytes to the client? You could perfectly well close the FIFO without sending that.
Also note that writing on a FIFO where there isn't a reader will generate a SIGPIPE signal — and you aren't handling those. The default action for SIGPIPE is to exit.

Writing to a pipe gets you a SIGPIPE if there's no reader; you need to have the server open the pipe for reading, so there is a reader (which doesn't read anything, but it exists).

Related

Trouble getting communication using a middleman program and file descriptors

FINAL UPDATE:
Code updated with final working version, got everything working thanks to the code found on: How to flush stdin without requiring user input?
Im programming C and having some trouble redirecting output from a parent process to a child process using file descriptors.
The idea of all of this, is for program A to be the middleman between all the data that goes from two programs.
I have a process A that creates a child process B which executes a execlp to a program C. There is also a program D that communicates with process A by named pipes and the idea is to redirect this communication to program C using unnamed pipes.
Right now my programs redirect the inicial communication from C to A to D correctly and from D to A correctly but fails when the redirection from A to C is supposed to happen.
I think the problem is that fgets() does not seem to retrieve the input unless two enters are given. I've tried using scanf, fscanf, getchar() and others aswell as flushing in multiple ways, nothing worked.
The only problem really is the fact that two inputs seem to be required for the communication of A to C to occur.
There are a million posts about this, and i've tried a lot of them to no sucess.
Can anyone help? Sorry if it sounds confusing.
Process A:(middleman)
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
int main(void)
{ //0->read 1->write
int fd_out[2];
int fd_in[2];
pipe(fd_out);
pipe(fd_in);
pid_t pid = fork(); //Create process B
char s[BUFSIZ];
char s2[BUFSIZ];
if ( pid == 0 )
{
close(STDOUT_FILENO);
dup(fd_out[1]);
close(fd_out[0]);
close(STDIN_FILENO);
dup(fd_in[0]);
close(fd_in[1]);
if(execlp("./test_pipe_game","./test_pipe_game",(char*)NULL) == -1){
printf("Error EXECL\n"); //Program C
}
}else{
int fifo;
char * myfifo = "/tmp/fifo123";
mkfifo(myfifo, 0666);
close (fd_out[1]);
close(fd_in[0]);
while(1){
read(fd_out[0], s, BUFSIZ);
printf("Game said:\n%s \nsending to client...\n", s);
fifo = open(myfifo, O_WRONLY);
write(fifo, s, BUFSIZ);
close(fifo);
fifo = open(myfifo, O_RDONLY);
read(fifo, s2, BUFSIZ);
close(fifo);
printf("Client said:\n%s \nsending to game...\n", s2);
write(fd_in[1], s2, BUFSIZ);
fflush(stdout);
printf("Sent!\n");
}
}
int status;
waitpid(pid, &status, 0);
if ( WIFEXITED(status) )
{
int exit_status = WEXITSTATUS(status);
printf("Exit status of the child was %d, my pid = %d\n",
exit_status, getpid());
}
return 0;
}
Program C:(game)
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
//https://stackoverflow.com/questions/54299405/how-to-flush-stdin-without-requiring-user-input
int flush_in(FILE *file)
{
int ch;
int flags;
int fd;
fd = fileno(file);
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
return -1;
}
do {
ch = fgetc(file);
} while (ch != EOF);
clearerr(file);
if (fcntl(fd, F_SETFL, flags)) {
return -1;
}
return 0;
}
int main(){
char s[BUFSIZ];
char aux;
int c;
int i = 0;
while(1){
printf("Say something client!\n");
fflush(stdout);
//fgets(s, BUFSIZ, stdin);
scanf("%s",s);
printf("I received %s from the client!\n", s);
flush_in(stdin);
}
}
Program D(client)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
int main()
{
int fd1;
char * myfifo = "/tmp/fifo123";
char str1[BUFSIZ];
char str2[BUFSIZ];
while (1)
{
system("clear");
fd1 = open(myfifo,O_RDONLY);
read(fd1, str1, BUFSIZ);
close(fd1);
printf("The game said:\n%s\n", str1);
printf("Say something:\n");
//fgets(str2, BUFSIZ, stdin);
scanf("%s",str2);
fd1 = open(myfifo,O_WRONLY);
write(fd1, str2, BUFSIZ);
close(fd1);
}
return 0;
}
UPDATE:
Like #CraigEstey and #thebusybee said my problem was using only one pipe, when i added the second pipe it solved the issue. I now have another problem, which seems to be related to reading from stdin without getting trash, i get no trash from the first 2 or 3 communications but after that fgets only reads trash. Flushing stdin does not seem to solve much. I've updated the codes if anyone wants to help!

Redirecting stdin with FIFO (names pipe)

I'm creating a C program with a server-client bases.
I've been trying to redirect the stdin to a named pipe I created and I've managed to put a client writing to the pipe. On the server side I opened the same pipe, closed stdin and redirected the stdin, using dup (tried with dup2 as well), to the pipe.
I have to read the input with the function getline. The problem is it reads the first input correctly, but recieves only nulls after it. I'll add a sample to the question.
server:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
main () {
char* str;
size_t size=0;
int pshell_in;
unlink("/tmp/par-shell-in");
if(mkfifo("/tmp/par-shell-in", 0777) < 0){
fprintf(stderr, "Error: Could not create pipe\n");
exit(-1);
}
if((pshell_in = open("/tmp/par-shell-in", O_CREAT | O_RDONLY, S_IRUSR)) < 0){
fprintf(stderr, "Error: Failed to open file\n");
exit(-1);
}
dup2(pshell_in, 0);
close(pshell_in);
while(1) {
if (getline(&str, &size, stdin)<0) {
printf("Oh dear, something went wrong with getline()! %s\n", strerror(errno));
return -1;
}
printf("%s", str);
}
}
* I know its null cause I've printed it with read (instead of redirecting) and it prints (null).
client:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#define VECTORSIZE 7
int main() {
char* buf;
int pshell_in;
size_t size=0;
if((pshell_in = open("/tmp/par-shell-in", O_WRONLY, S_IWUSR)) < 0){
fprintf(stderr, "Error: Failed to open file\n");
exit(-1);
}
printf("%d\n", pshell_in);
while(1) {
if (getline(&buf, &size, stdin) < 0) {
return -1;
}
write(pshell_in, buf, 256);
}
}
I suspect its right because if I use read on the client side (replacing O_WRONLY with O_RDWR) it prints the string as I typed it.
Can anyone help me with this one?
FIFOs are funny things. If a process tries to open one for reading, it will block until there's a process that opens it for writing. Conversely, if a process tries to open one for writingt, it will block until there's a process that opens it for reading. However, multiple processes can open it for reading or writing. When there are no more processes with it open for reading, writes will fail; when there are no more processes with it open for writing, reads will fail. And when the operations fail, you have to close and reopen the FIFO to continue processing data afresh.
I strongly suspect you're running into problems because of these behaviours.
Additionally, your client write code is dubious; you aren't paying any attention to how much data was read. You have:
while(1) {
if (getline(&buf, &size, stdin) < 0) {
return -1;
}
write(pshell_in, buf, 256);
}
If, as is probable, you read less than 256 characters of input in the line, then it's quite possible that you go writing beyond the bounds of the array that was allocated by getline(). It's also distinctly possible that some or even most of that data is null bytes. However, the (null) you're seeing in the server typically indicates that you're trying to print a string but passed printf() a null pointer. Whatever's going on, most of it is undefined behaviour which is a Bad Thing™ and should be avoided at all costs.
You should have something more like:
ssize_t nbytes;
while ((nbytes = getline(&buf, &size, stdin)) > 0)
{
if (write(pshell_in, buf, nbytes) != nbytes)
{
fprintf(stderr, "Short write to FIFO\n");
break;
}
}
free(buf);
Note how this only writes as much data as was read and doesn't assume that 256 bytes were available to be written.

writing to stdin of another program/reading from stdout of another program in c

It's a homework question. I have 3 programs A, B and C, they are not father/child processes, but separate programs. B must write a message ("Hello") to the stdin of A and read another message ("Hello") from stdout of C. Which concept i should use to implement it? I looked around for some time but i couldn't find anything proper. I thought i should use FIFO, but i couldn't redirect pipes. Can i use dup2 with FIFO? if yes, how? It's not the homework's itself, it just the way it should work. Then I will implement other things on it. (I can post my fifo base code, if it's the case, just don't do it now because i'm not sure)
Let me know if I missunderstood the question.
Let's say you create programs A, B and C. Programmatically, you could use man 1 mkfifo or man 3 mkfifo, to create named pipes.
Then each one of your processes would open(2) them and use dup2(2) according to their needs.
For instance, program A could redirect its stdout like this:
int fifo = open("fifo_1", O_WRONLY);
dup2(fifo, 1);
or program B could redirect both its stdin and stdout like this:
int fifo_in = open("fifo_1", O_RDONLY);
int fifo_out = open("fifo_2", O_WRONLY);
dup2(fifo_in, 0);
dup2(fifo_out, 1);
or whatever else you need.
I modified this code as #chrk explained. It's a FIFO example from the book "Advanced Unix Programming". A simple server-client example. Client sends three lowerletter strings to the server, server make them upperletter and send back to the client via FIFO("fifo#clientpid" is the name of client's fifo). Client prints them to its stdout. So i modified it in a way that after receiving the message from client, server writes upperletter strings to the STDINT of the client. And client reads them from STDINT and print to its stdout. It works as i expected. Thanks for all help. First i implemented in a wrong way. With the help of chrk's code i wrote it again. Here is the code:
client's output
client 2941 started
client 2941: applesauce --> APPLESAUCE
client 2941: tiger --> TIGER
client 2941: mountain --> MOUNTAIN
Client 2941 done
server:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#define ec_neg1(s,m) if((s) == -1) {perror(m); exit(errno);}
#define SERVER_FIFO_NAME "fifo_server"
#define PERM_FILE 0664
struct simple_message {
pid_t sm_clientpid;
char sm_data[200];
};
int main()
{
int fd_server, fd_client, i;
ssize_t nread;
struct simple_message msg;
char fifo_name[100];
printf("server started\n");
if (mkfifo(SERVER_FIFO_NAME, PERM_FILE) == -1 && errno != EEXIST)
{perror("can't make fifo"); exit(errno); }
ec_neg1( fd_server = open(SERVER_FIFO_NAME, O_RDWR), "cant open fd_server" )
while (1)
{
ec_neg1( nread = read(fd_server, &msg, sizeof(msg)), "can't read from fd_server")
if (nread == 0) {
errno = ENETDOWN;
perror("nread == 0"); exit(errno);
}
for (i = 0; msg.sm_data[i] != '\0'; i++)
msg.sm_data[i] = toupper(msg.sm_data[i]);
make_fifo_name(msg.sm_clientpid, fifo_name, sizeof(fifo_name));
ec_neg1( fd_client = open(fifo_name, O_WRONLY), "can't open fifo_name" )
ec_neg1( write(fd_client, &msg, sizeof(msg)), "can't write to fd_client" )
ec_neg1( close(fd_client), "can't close fd_client" )
}
/* never actually get here */
ec_neg1( close(fd_server), "can't close fd_server" )
exit(EXIT_SUCCESS);
return 0;
}
int make_fifo_name(pid_t pid, char *name, size_t name_max)
{
snprintf(name, name_max, "fifo%ld", (long)pid);
return 0;
}
client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ec_neg1(s,m) if((s) == -1) {perror(m); exit(errno);}
#define SERVER_FIFO_NAME "fifo_server"
#define PERM_FILE 0664
struct simple_message {
pid_t sm_clientpid;
char sm_data[200];
};
int make_fifo_name(pid_t pid, char *name, size_t name_max)
{
snprintf(name, name_max, "fifo%ld", (long)pid);
return 0;
}
int main()
{
int fd_server, fd_client = -1, i;
ssize_t nread;
struct simple_message msg;
char fifo_name[100];
char *work[] = {
"applesauce",
"tiger",
"mountain",
NULL
};
printf("client %ld started\n", (long)getpid());
msg.sm_clientpid = getpid();
make_fifo_name(msg.sm_clientpid, fifo_name,
sizeof(fifo_name));
if (mkfifo(fifo_name, PERM_FILE) == -1 && errno != EEXIST)
{perror("can't make fifo"); exit(errno); }
ec_neg1( fd_server = open(SERVER_FIFO_NAME, O_WRONLY), "can't open fd_server" )
for (i = 0; work[i] != NULL; i++)
{
strcpy(msg.sm_data, work[i]);
ec_neg1( write(fd_server, &msg, sizeof(msg)),"can't write to fd_server" )
if (fd_client == -1){
ec_neg1( fd_client = open(fifo_name, O_RDWR), "can't open fifo_name" )
ec_neg1(dup2(fd_client, 0), "can't duplicate stdin")
}
ec_neg1( nread = read(0, &msg, sizeof(msg)), "can't read from fd_client" )
if (nread == 0) {
errno = ENETDOWN;
perror("nread == 0"); exit(errno);
}
printf("client %ld: %s --> %s\n", (long)getpid(),
work[i], msg.sm_data);
}
ec_neg1( close(fd_server), "can't close fd_server" )
ec_neg1( close(fd_client), "can't close fd_client" )
ec_neg1( unlink(fifo_name), "can't unlink fifo_name" )
printf("Client %ld done\n", (long)getpid());
exit(EXIT_SUCCESS);
}

Named pipes without child process

I used a FIFO for a simple read/write programme where the input from user is written to standard output by the writer function. The question is however, am I able to run this program without creating a child process (with the fork() operation). From what I see from examples about FIFOs, most read/write programmes with a named pipe/FIFO are done with 2 files - one for reading and one for writing. Could I do these all in a file?
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/* read from user */
void reader(char *namedpipe) {
char c;
int fd;
while (1) {
/* Read from keyboard */
c = getchar();
fd = open(namedpipe, O_WRONLY);
write(fd, &c, 1);
fflush(stdout);
}
}
/* writes to screen */
void writer(char *namedpipe) {
char c;
int fd;
while (1) {
fd = open(namedpipe, O_RDONLY);
read(fd, &c, 1);
putchar(c);
}
}
int main(int argc, char *argv[]) {
int child,res;
if (access("my_fifo", F_OK) == -1) {
res = mkfifo("my_fifo", 0777);
if (res < 0) {
return errno;
}
}
child = fork();
if (child == -1)
return errno;
if (child == 0) {
reader("my_fifo");
}
else {
writer("my_fifo");
}
return 0;
}
You'll need to put a lock on the file, or else you could attempt to be reading when someone else is writing. You'll also want to flush the write buffer, or your changes to the fifo might actually not be recorded until the kernel write buffer fills and then writes to the file (in linux, write doesn't guarantee a write happens at that exact moment. i see you're flushing stdout, but you should also fsync on the file descriptor. This will cause the file to lock during any write operation so that no one else can write. In order to lock the file for reading, you might have to use a semaphore.

How to send a simple string between two programs using pipes?

I tried searching on the net, but there are hardly any resources. A small example would suffice.
EDIT
I mean, two different C programs communicating with each other. One program should send "Hi" and the other should receive it. Something like that.
A regular pipe can only connect two related processes. It is created by a process and will vanish when the last process closes it.
A named pipe, also called a FIFO for its behavior, can be used to connect two unrelated processes and exists independently of the processes; meaning it can exist even if no one is using it. A FIFO is created using the mkfifo() library function.
Example
writer.c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fd;
char * myfifo = "/tmp/myfifo";
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
/* write "Hi" to the FIFO */
fd = open(myfifo, O_WRONLY);
write(fd, "Hi", sizeof("Hi"));
close(fd);
/* remove the FIFO */
unlink(myfifo);
return 0;
}
reader.c
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#define MAX_BUF 1024
int main()
{
int fd;
char * myfifo = "/tmp/myfifo";
char buf[MAX_BUF];
/* open, read, and display the message from the FIFO */
fd = open(myfifo, O_RDONLY);
read(fd, buf, MAX_BUF);
printf("Received: %s\n", buf);
close(fd);
return 0;
}
Note: Error checking was omitted from the above code for simplicity.
From Creating Pipes in C, this shows you how to fork a program to use a pipe. If you don't want to fork(), you can use named pipes.
In addition, you can get the effect of prog1 | prog2 by sending output of prog1 to stdout and reading from stdin in prog2. You can also read stdin by opening a file named /dev/stdin (but not sure of the portability of that).
/*****************************************************************************
Excerpt from "Linux Programmer's Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: pipe.c
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
write(fd[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
return(0);
}
dup2( STDIN_FILENO, newfd )
And read:
char reading[ 1025 ];
int fdin = 0, r_control;
if( dup2( STDIN_FILENO, fdin ) < 0 ){
perror( "dup2( )" );
exit( errno );
}
memset( reading, '\0', 1025 );
while( ( r_control = read( fdin, reading, 1024 ) ) > 0 ){
printf( "<%s>", reading );
memset( reading, '\0', 1025 );
}
if( r_control < 0 )
perror( "read( )" );
close( fdin );
But, I think that fcntl can be a better solution
echo "salut" | code
What one program writes to stdout can be read by another via stdin. So simply, using c, write prog1 to print something using printf() and prog2 to read something using scanf(). Then just run
./prog1 | ./prog2
Here's a sample:
int main()
{
char buff[1024] = {0};
FILE* cvt;
int status;
/* Launch converter and open a pipe through which the parent will write to it */
cvt = popen("converter", "w");
if (!cvt)
{
printf("couldn't open a pipe; quitting\n");
exit(1)
}
printf("enter Fahrenheit degrees: " );
fgets(buff, sizeof (buff), stdin); /*read user's input */
/* Send expression to converter for evaluation */
fprintf(cvt, "%s\n", buff);
fflush(cvt);
/* Close pipe to converter and wait for it to exit */
status=pclose(cvt);
/* Check the exit status of pclose() */
if (!WIFEXITED(status))
printf("error on closing the pipe\n");
return 0;
}
The important steps in this program are:
The popen() call which establishes the association between a child process and a pipe in the parent.
The fprintf() call that uses the pipe as an ordinary file to write to the child process's stdin or read from its stdout.
The pclose() call that closes the pipe and causes the child process to terminate.
This answer might be helpful for a future Googler.
#include <stdio.h>
#include <unistd.h>
int main(){
int p, f;
int rw_setup[2];
char message[20];
p = pipe(rw_setup);
if(p < 0){
printf("An error occured. Could not create the pipe.");
_exit(1);
}
f = fork();
if(f > 0){
write(rw_setup[1], "Hi from Parent", 15);
}
else if(f == 0){
read(rw_setup[0],message,15);
printf("%s %d\n", message, r_return);
}
else{
printf("Could not create the child process");
}
return 0;
}
You can find an advanced two-way pipe call example here.
First, have program 1 write the string to stdout (as if you'd like it to appear in screen). Then the second program should read a string from stdin, as if a user was typing from a keyboard. then you run:
$ program_1 | program_2

Resources