Program using named pipe in background doesn't close properly - c

I am trying to write program which part is communication using named pipe (fifo). When I am running writer and reader program in infinite loop (writer in background) in bash script, some time writer program is not closing properly by what I receive bad results.
There is a simple code which I try to use in my program too:
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;
}
run.sh
#!/bin/bash
while true; do
./writer &
./reader
done
Could you help me to do this properly or maybe have you another idea how to do this?
Regards

Related

Connection many client fifo to one server fifo

have to write two programs (a client and a server) which will do
chatting with each other using FIFOs (to pass message from one process to another). The
server process creates a SERVER_FIFO to receive client connections only. The server
maintains the list of online clients. Each client creates its own CLIENT_FIFO to receive
commands from server to be executed at client using system() system call. You can use
getpid() system call to retrieve client’s process id to be concatenated in the
CLIENT_FIFO name.
I only to create 2 fifo that communicate with each other
SERVER
// C program to implement one side of FIFO
// This side writes first, then reads
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fd;
char * myfifo = "/tmp/myfifo";
mkfifo(myfifo, 0666);
char arr1[80], arr2[80];
while (1)
{
fd = open(myfifo, O_WRONLY);
fgets(arr2, 80, stdin);
write(fd, arr2, strlen(arr2)+1);
close(fd);
// Open FIFO for Read only
fd = open(myfifo, O_RDONLY);
// Read from FIFO
read(fd, arr1, sizeof(arr1));
// Print the read message
printf("User2: %s\n", arr1);
close(fd);
}
return 0;
}
==================================================================
CLIENT
// C program to implement one side of FIFO
// This side reads first, then reads
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fd1;
// FIFO file path
char * myfifo = "/tmp/myfifo";
mkfifo(myfifo, 0666);
char str1[80], str2[80];
while (1)
{
// First open in read only and read
fd1 = open(myfifo,O_RDONLY);
read(fd1, str1, 80);
// Print the read string and close
printf("User1: %s\n", str1);
close(fd1);
fd1 = open(myfifo,O_WRONLY);
fgets(str2, 80, stdin);
write(fd1, str2, strlen(str2)+1);
close(fd1);
}
return 0;
}

Using named pipes in c

i have this simple program that passes a value through a named pipe from child to parent process:
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdio.h>
int main()
{
char * myfifo = "/home/tmp/myfifo";
mkfifo(myfifo, 0666);
int fd,rec;
pid_t c=fork();
if(c==0){
fd = open(myfifo, O_WRONLY);
rec=100;
write(fd, rec, sizeof(rec));
}
if(c>0){
sleep(1);
fd = open(myfifo, O_RDONLY);
read(fd, rec, sizeof(rec));
printf("%d\n",fd);
printf("%d\n",rec);
}
}
This program prints fd=-1 and instead of rec being 100 it prints rec's address.I also tried putting &rec in read and write but it did not solve anything.What am i doing wrong?
There's an issue with this line:
write(fd, rec, sizeof(rec));
This is the prototype of write():
ssize_t write(int fd, const void *buf, size_t count);
That means that you're reading from the memory location stored in rec, not the content of rec.
The same thing applies for read(). You need to pass a pointer to rec instead of rec itself.
Also, always make sure to close files after you open and perform I/O on them.
Here's a correct copy of your code:
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdio.h>
int main()
{
const char *myfifo = "/home/tmp/myfifo";
mkfifo(myfifo, 0666);
int fd, rec;
pid_t c = fork();
if(c == 0) {
fd = open(myfifo, O_WRONLY);
rec = 100;
write(fd, &rec, sizeof(rec));
close(fd);
}
if(c > 0) {
sleep(1);
fd = open(myfifo, O_RDONLY);
read(fd, &rec, sizeof(rec));
printf("%d\n", fd);
printf("%d\n", rec);
close(fd);
}
}
Of course, always make sure you have the proper permissions to create, read, and write files in that directory. Also, make sure the directory /home/tmp exists.

is there a way of knowing if a file descriptor opened for write has someone listening on the read side?

i have fifo open in one side for read and in the other side for write, the read side close the fd he opened . is there a way of know if the reader closed this fd in the writer side ?
i want that the writer will have any notification about whether the reader is ready to read because if not my writer will get blocked on write .
writer.c :
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd;
char * myfifo = "/tmp/fifo_pipe";
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
/* write "Hi" to the FIFO */
fd = open(myfifo, O_WRONLY);
write(fd, "Hey", sizeof("Hey"));
/*here is there a posibilty of know that the read side hase close hi's side of the pipe before write? */
write(fd, "test\n", strlen("test\n"));
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/fifo_pipe";
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;
}
A write in a FIFO with no reader will raise SIGPIPE and eventually returns -1 and errno set to EPIPE.
You can use lsof system command to check information about files opened by processes. Extract the FD field of the lsof command output and proceed.
For more details and example, refer this link
In your case, to get the number of process listening to your fifo, try executing the below system command.
lsof /tmp/fifo_pipe | grep [0-9]r | wc -l
In C you can implement something like this:
int i = 0;
FILE *fp;
char *command = "lsof /tmp/rjfifo | grep [0-9]r | wc -l";
fp = popen(command,"r");
if (fp != NULL){
fscanf(fp,"%d",&i);
}
fclose(fp);
printf("Number of processes reading /tmp/fifo_pipe = %d \n",i);

IPC FIFO Producer-Consumer Deadlock

This is the producer.
// speak.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define FIFO_NAME "american_maid"
int main(void)
{
char s[300];
int num, fd;
mknod(FIFO_NAME, S_IFIFO | 0666, 0);
printf("waiting for readers...\n");
fd = open(FIFO_NAME, O_WRONLY);
printf("got a reader--type some stuff\n");
while (gets(s), !feof(stdin)) {
if ((num = write(fd, s, strlen(s))) == -1)
perror("write");
else
printf("speak: wrote %d bytes\n", num);
}
return 0;
}
And this is the consumer.
//tick.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define FIFO_NAME "american_maid"
int main(void)
{
char s[300];
int num, fd;
mknod(FIFO_NAME, S_IFIFO | 0666, 0);
printf("waiting for writers...\n");
fd = open(FIFO_NAME, O_RDONLY);
printf("got a writer\n");
do {
if ((num = read(fd, s, 300)) == -1)
perror("read");
else {
s[num] = '\0';
printf("tick: read %d bytes: \"%s\"\n", num, s);
}
} while (num > 0);
return 0;
}
When I run them, Producer outputs,
waiting for readers...
And consumer outputs,
waiting for writers...
speak doesn't find the reader, tick. As from the theory here I got that, open() (speak.c) will be keep blocked until open() (tick.c) is opened. And the vice versa. So I guess there a deadlock or something happening. I need a solution of this.
It looks like you have a race condition between the reader and the writer.
To fix this, you need a method of not launching the reader until the writer is "active". For this, I'd suggest making a pipe and writing to it when the writer is ready. Then, when reading from the read end of the fork succeeds, the fifo is prepared and the reader should work.
You need to use forks here because coordinating mutexes between a parent and a child process is non-trivial and properly done pipes is easier.
Also, you called mknod() twice. Granted, it'll return -1 with errno == EEXIST, but be more careful. To avoid this, make the reader and writer a function that takes a path as an argument.
Rewrite your writer as int speak(const char *fifo, int pipefd) and your reader as int tick(const char *fifo).
Then make a wrapper like this:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
const char fifo_name[] /* = ... */;
int speak(const char *fifo, int pipefd);
int tick(const char *fifo);
int main() {
int pipefd[2];
pipe(pipefd);
mknod(fifo_name, S_IFIFO | 0666, 0);
if (fork() == 0) {
close(pipefd[0]);
return speak(fifo_name, pipefd[1]);
} else {
close(pipefd[1]);
char foo;
read(pipefd[0], &foo, 1);
return tick(fifo_name);
}
}
Modify your writer to print a byte (of anything) to the passed fd after the fifo is created (i.e. right after the call to open(..., O_WRONLY)).
Don't use my code verbatim, as I've omitted error checking for the sake of brevity.
it runs ok in my env. and if reader and writer is ready, open will return. because open is blocked, so in my opinion, mknod function is success. May be you excute these two process at different path.

Proper FIFO client-server connection

I'm trying to write simple client and server C programs, communicating with each other in separate terminals.
The server has to create a public fifo and wait for the client. Meanwhile the client is creating his own fifo through which the server's response will come. The task of the client is sending the server a name created by the queue and get in return the result of the ls command.
I did search for an answer, for example: fifo-server-program, example-of-using-named-pipes-in-linux-bash, how-to-send-a-simple-string-between-two-programs-using-pipes. I started with the code from the third link and slowly modified it.
What I've got now, is a client taking input from the user, sending it to the server and receiving it back. But it only works once. I have no idea why. The body of main function is below. I will be grateful for any help.
EDIT:
I got it working! :D The codes are below, maybe it will help someone.
The server.c code:
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char* argv[])
{
int fds[2];
char tab[BUFSIZ];
int fd, n;
char *myfifo = "/tmp/serwer";
char *myfifo2 = "/tmp/client";
pipe(fds);
mkfifo(myfifo,0666);
while(1)
{
fds[0]=open(myfifo2,O_RDONLY);
fds[1]=open(myfifo,O_WRONLY);
read(fds[0],tab,BUFSIZ);
if (strcmp("klient",tab)==0) {
printf("Od klienta: %s\n",tab);
fd=open(tab,O_WRONLY);
if(fork()==0)
{
dup2(fds[1],1);
close(fds[1]);
execlp("ls","ls","-l",NULL);
close(fds[0]);
close(fds[1]);
}
else
{
dup2(fds[0],0);
n = read(fds[0],tab,BUFSIZ);
write(fd,tab,n);
close(fds[0]);
close(fds[1]);
}
}
memset(tab, 0, sizeof(tab));
close(fd);
close(fds[0]);
close(fds[1]);
}
unlink(myfifo);
return 0;
}
The client.c code:
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char* argv[])
{
int fds[2];
char *myfifo = "/tmp/serwer";
char *myfifo2 = "/tmp/client";
mkfifo(myfifo2,0666);
fds[0]=open(myfifo,O_RDONLY);
fds[1]=open(myfifo2,O_WRONLY);
char tab[BUFSIZ];
memset(tab, 0, sizeof(tab));
write(fds[1],"klient",6);
perror("Write:"); //Very crude error check
read(fds[0],tab,sizeof(tab));
perror("Read:"); // Very crude error check
printf("Odebrano od serwera: %s\n",tab);
close(fds[0]);
close(fds[1]);
unlink(myfifo2);
return 0;
}
Why don't you just manage both fifo's in the server? Simply changing your code to do this makes it work correctly.
If you actually want to have a client-server relationship, with a server serving many different clients, sockets would probably be a better choice.
client.cpp
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char str[BUFSIZ];
printf("Input message to serwer: ");
scanf("%s", str);
/* write str to the FIFO */
client_to_server = open(myfifo, O_WRONLY);
server_to_client = open(myfifo2, O_RDONLY);
write(client_to_server, str, sizeof(str));
perror("Write:"); //Very crude error check
read(server_to_client,str,sizeof(str));
perror("Read:"); // Very crude error check
printf("...received from the server: %s\n",str);
close(client_to_server);
close(server_to_client);
/* remove the FIFO */
return 0;
}
server.cpp
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int main()
{
int client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char buf[BUFSIZ];
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
mkfifo(myfifo2, 0666);
/* open, read, and display the message from the FIFO */
client_to_server = open(myfifo, O_RDONLY);
server_to_client = open(myfifo2, O_WRONLY);
printf("Server ON.\n");
while (1)
{
read(client_to_server, buf, BUFSIZ);
if (strcmp("exit",buf)==0)
{
printf("Server OFF.\n");
break;
}
else if (strcmp("",buf)!=0)
{
printf("Received: %s\n", buf);
printf("Sending back...\n");
write(server_to_client,buf,BUFSIZ);
}
/* clean buf from any data */
memset(buf, 0, sizeof(buf));
}
close(client_to_server);
close(server_to_client);
unlink(myfifo);
unlink(myfifo2);
return 0;
}
It only works once because of how named pipes work. Each time you open a named pipe for read you block until another process opens it for write. Then you are paired up and the file descriptor connects your processes. Once either end closes that connection that's the end of that pipe. In order for your server to "accept another connection" it needs to move the open and close of the pipes into its main loop so it can be paired up over and over.

Resources