Reputation: 4280
I have a loop. Inside this loop I am trying to detect if a read or write is triggered on a named pipe (FIFO) file by using select()
.
If a read is triggered I call read()
on the FIFO file descriptor.
If a write is triggered I call write()
on the FIFO file descriptor.
The issue is that if a write occurs and I write to the FIFO, it will trigger a read. And then when I read from the FIFO it will trigger a write. Causing an infinite loop.
This loop occurs immediately if I use the same file descriptor in mode O_RDWR
. This loop occurs after the first write if I create a separate file descriptor for both reading and writing.
#include <errno.h>
#include <sys/select.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
int main() {
// Open export fifo
int fd = open("./foo-fifo", O_RDWR | O_CREAT);
if (fd < 0) { // Failed to open
perror("error opening fifo");
}
// Read or write fifo until "quit" is in buffer
while (true) {
fd_set read_fds;
fd_set write_fds;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
FD_ZERO(&write_fds);
FD_SET(fd, &write_fds);
int num_fds = select(fd+1, &read_fds, &write_fds, NULL, NULL);
if (num_fds < 0) { // Failed to select
perror("failed to select fifo fd");
} else if (num_fds == 0) { // Timeout
continue;
}
// If read
if (FD_ISSET(fd, &read_fds)) {
char buf[1000] = "";
if (read(fd, buf, sizeof(buf)) < 0) {
perror("error reading fifo");
}
printf("read: \"%s\"\n", buf);
if (strcmp(buf, "quit\n") == 0) {
break;
}
}
// If write
if (FD_ISSET(fd, &write_fds)) {
char *buf = "foo";
if (write(fd, buf, sizeof(buf)) < 0) {
perror("error writing fifo");
}
printf("write: \"%s\"\n", buf);
}
}
// Close fifo
if (close(fd) < 0) { // Failed to close
perror("failed to close export fifo");
}
return 0;
}
Run the example by downloading the code from here (GitHub Gist). Then run:
gcc -o fifo fifo.c
./fifo
The output will show a loop between reading and writing:
write: "foo"
read: ""
write: "foo"
read: ""
write: "foo"
...
Upvotes: 0
Views: 875
Reputation: 33601
Note: This is prefaced by my top comments.
We need two processes (e.g. a server and a client).
fifos are single direction (a writer and a reader), not like a socket.
So, to do this with fifos, you'll need two of them. (e.g.) Given processes A and B, we need two pipes/fifos: pipeAB
and pipeBA
.
Process A writes to pipeAB
and B reads from pipeAB
.
Process B writes to pipeBA
and A reads from pipeBA
If you want to use a socket, you could do a PF_UNIX
(aka AF_UNIX
) socket. See man 7 unix
and man 2 socketpair
.
Or, you could do a full blown AF_INET socket with the host set to localhost with some fixed port number.
As an exercise [for you], consider doing it in several ways. That is, an argv
option like -Tp
for dual pipes, -Tu
for AF_UNIX, and -Ts
for AF_INET, etc. Only the initialization would be different. The protocol would be nearly identical otherwise.
For AF_UNIX
sockets, if the client and server are different programs, it may be easier to create a file of type socket in the file system. This can be done by filling in a struct sockaddr_un
with the "filename" and then using bind
after the socket
call. See: https://www.ibm.com/support/knowledgecenter/en/SSB23S_1.1.0.13/gtpc1/unixsock.html for an example
Upvotes: 1