PRETEND THEY'RE NOT PARENT AND CHILD PROCESSES EVEN THOUGH THEY ARE.
MAKE FIFO:
/* Create response FIFO. */
if (mkfifo(RESP_FIFO_NAME, FIFO_MODE) == -1) {
if (errno != EEXIST) {
fprintf(stderr, "Server: Couldn’t create %s FIFO.\n", RESP_FIFO_NAME);
exit(1);
}
}
Fork:
/* 3. Fork the client process. */
switch (fork()) {
/* Fork failed. */
case (pid_t) -1:
fprintf(stderr, "Call to fork failed.\n");
exit(1);
/* Client (child) process. */
case 0:
execlp(CLIENT_NAME, CLIENT_NAME, argv[SERVER_CMD_FILE_ARG], argv[SERVER_LOG_FILE_ARG], NULL);
/* Server (parent) Process */
default:
sleep(1);
server(infd, outfd, argv[INIT_DB_ARG], argv[FINAL_DB_ARG]);
} /* End of switch. */
server function:
int server(int infd, int outfd, char *init_db_name, char *final_db_name) {
...
if ((outfd = open(RESP_FIFO_NAME, O_WRONLY)) == -1) {
fprintf(stderr, "Server: Failed to open %s FIFO.\n", RESP_FIFO_NAME);
perror(RESP_FIFO_NAME);
exit(1);
}
...
}
client program:
printf("RESP_FIFO FILE DESCRIPTOR: %d\n", infd);
/* Open the response FIFO for reading. */
if ((infd = open(RESP_FIFO_NAME, O_RDONLY)) == -1) {
fprintf(stderr, "Client: Failed to open %s FIFO.\n", RESP_FIFO_NAME);
exit(1);
}
else printf("RESP_FIFO FILE DESCRIPTOR: %d\n", infd);
TL;DR The open for reading call in client program is not being executed before the open for writing call in the server program.
Are you opening the response fifo for writing before its other end is open for reading? See fex. Having a trouble with opening FIFO in C
Either wait until you know the FIFO is open for reading or make the open blocking, to wait for the client. Also make sure the server has write permission for the FIFO file.
What about pipe() for this
From pipe(2):
Create descriptor pair for interprocess communication.
The pipe() function creates a pipe (an object that allows unidirectional
data flow) and allocates a pair of file descriptors. The first descrip-
tor connects to the read end of the pipe; the second connects to the
write end.
Data written to fildes1 appears on (i.e., can be read from) fildes[0].
You can look how memcached use it
Yes, without a reader, an open() of a filesystem FIFO for writing will either block or, in the nonblocking case, fail with ENXIO.
You have at least two easy options.
First, you could open the "command" FIFO, nonblocking, for reading in addition to writing, either O_RDWR or with two separate file descriptors, one O_RDONLY and one O_WRONLY.
Second, you could use a filesystem socket instead, and have the server listen there. That gives you a bi-directional communication channel over one ofile.
UNIX gives you other options, too — message queues or files or shared memory segments, perhaps using signals for one interlocutor to prod the other, come to mind — but the above are quite straightforward.
I wrote into a FIFO on one end without reading out of the other end. Just having the files open for reading and writing is not enough, you have to actually read the text out of the FIFO or the program will incur an error (ENXIO if you have O_NONBLOCK flag set).
Related
I'm currently trying to figure out how to correctly close a file descriptor when it points to a remote file and the connection is lost.
I have a simple example program which opens a file descriptor on a sshfs mount folder and start to write to the file.
I'm not able to find how to handle the case when the connection is lost.
void *write_thread(void* arg);
int main()
{
pthread_t thread;
int fd = -1;
if(-1 == (fd = open("/mnt/testfile.txt", O_CREAT | O_RDWR | O_NONBLOCK, S_IRWXU)))
{
fprintf(stderr, "Error oppening file : %m\n");
return EXIT_FAILURE;
}
else
{
if(0 > pthread_create(&thread, NULL, write_thread, &fd))
{
fprintf(stderr, "Error launching thread : %m\n");
return EXIT_FAILURE;
}
fprintf(stdout, "Waiting 10 seconds before closing\n");
sleep(10);
if(0 > close(fd))
{
fprintf(stderr, "Error closing file descriptor: %m\n");
}
}
}
void *write_thread(void* arg)
{
int fd = *(int*)arg;
int ret;
while(1)
{
fprintf(stdout, "Write to file\n", fd);
if(0 > ( ret = write(fd, "Test\n", 5)))
{
fprintf(stderr, "Error writing to file : %m\n");
if(errno == EBADF)
{
if(-1 == close(fd))
{
fprintf(stderr, "Close failed : %m\n");
}
return NULL;
}
}
else if(0 == ret)
{
fprintf(stderr, "Nothing happened\n");
}
else
{
fprintf(stderr, "%d bytes written\n", ret);
}
sleep(1);
}
}
When the connection is lost (i.e. I unplug the ethernet cable between my boards), The close in the main thread always blocks whether I use the flag O_NONBLOCK or not.
The write call sometimes immediately fails with EBADF error or sometimes continues for a long time before failing.
My problem is that the write call doesn't always fail when the connection is lost so I can't trigger the event into the thread and I also can't trigger it from the main thread because close blocks forever.
So my question is : How to correctly handle this case in C ?
question is: how to correctly handle this case in C?
Simply you can not. File handles are designed to be unified and simple, no matter where they point to. When a device is mounted, and the connection (physical or virtual) to it crashes down, things become tricky even at the command line level.
There is a fundamental problem with remote filesystems, where on the one hand you have to cache things in order for performance remain at a usable level, and on the other hand caching in multiple clients can lead to conflicts that are not seen by the server.
NFS, for example, chooses caching by default and if the cache is dirty, it will simply hang until the connection resumes.
Documentation for sshfs suggests similar behavior.
From grepping sshfs' source code, it seems that it doesn't support O_NONBLOCK at all.
None of that has anything to do with C.
IMO your best option is to switch to nfs, and mount with e.g. -o soft -o timeo=15 -o retrans=1.
This could cause data corruption/loss in certain situations when there is a network disconnect, mainly when there are multiple clients or when the client crashes, but it does support O_NONBLOCK and in any case will return EIO if the connection is lost while a request is in-flight.
After some diggin around I found that the SSH mount could be configured to drop the connection and disconnect from server if nothing happens.
Setting ServerAliveInterval X on client side to disconnect if the server is unresponsive after X sec.
Setting ClientAliveCountMax X on server side to disconnect if the client is unresponsive after X sec.
ServerAliveCountMax Y and ClientAliveCountMax Y can also be used in order to retry Y times before dropping the connection.
With this configuration applied, the sshfs mount is automatically removed by Linux when the connection is unresponsive.
With this configuration, the write call fails with Input/output error first and then with Transport endpoint is not connected.
This is enough to detect that the connection is lost and thus cleaning up the mess before exiting.
I have read this post Determine between socket and fd, and the answer recommends creating a structure with two fields, is there another way to test if a descriptor is a socket or a regular file on windows ?
You could try something that should only work on a socket, and see if it fails with ENOTSOCK. getsockname() for example.
this could help:
replace stdin stdout stderr with a socket…
http://www6.uniovi.es/cscene/CS5/CS5-05.html
int exec_comm_handler( int sck )
{
close(0); /* close standard input */
close(1); /* close standard output */
close(2); /* close standard error */
if( dup(sck) != 0 || dup(sck) != 1 || dup(sck) != 2 ) {
perror("error duplicating socket for stdin/stdout/stderr");
exit(1);
}
printf("this should now go across the socket...\n");
execl( "/bin/sh", "/bin/sh", "-c", "/path/to/redirected_program" );
perror("the execl(3) call failed.");
exit(1);
}
Our 'comm handler' first closes the file descriptors for standard input,
standard output, and standard error. It then uses dup(2) to duplicate
the socket file handle. dup(2) will duplicate the given file descriptor
and return the duplicate as the next available descriptor. Since 0, 1,
and 2 are the next available descriptors, they should be the returned
duplicates. Now operations on stdin/stdout/stderr [0/1/2] will act
upon the socket instead of the original stdin/stdout/stderr.
I was unaware that this technique (calling dup(2) on a socket file
descriptor) was possible untill seeing code written by Martin Mares.
I am trying to create a simple example of named pipes (FIFO). Here, the server will listen for message from the client, which writes on the named pipe, common to both of them. The special thing to be implemented is that the FIFO should be non-blocking (usage of O_NONBLOCK).
By non blocking, I mean that the writer should return immediately after writing, if there is no reader. Similarly, reader should return immediately if there is no message(no writer).
I have created the blocking version though and its working fine. I am then trying to convert it to non-blocking.
Here's the client :
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define FIFO "/tmp/myFIFO"
/*
This acts as the client, writing to the FIFO
*/
int main(int argc, char *argv[])
{
FILE *fp;
int fifo_fd;
if(argc != 2)
{
printf("Usage : ./fifo_client <message> \n");
exit(1);
}
fifo_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
if(fifo_fd < 0)
{
perror("Error while open call");
exit(1);
}
fp = fdopen(fifo_fd, "w");
if(fp == NULL)
{
perror("Error while opening fd");
exit(1);
}
fputs(argv[1],fp);
/* Close the fp */
fclose(fp);
return 0;
}
Here's the server :
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define FIFO "/tmp/myFIFO"
/*
This acts as a server waiting for strings to be written by the client, over the FIFO
*/
int main()
{
FILE *fp;
int fifo_fd;
char buf[1024];
/* Create a FIFO */
umask(0);
if(mkfifo(FIFO,0666) < 0) /* 0666 is read and write permission*/
{
perror("Error creating FIFO");
exit(1);
}
while(1) /*endless wait, keep reading strings and print*/
{
fifo_fd = open(FIFO, O_RDONLY | O_NONBLOCK);
if(fifo_fd < 0)
{
perror("Error while open call");
exit(1);
}
fp = fdopen(fifo_fd, "w");
if(fp == NULL)
{
perror("Error while opening fd");
exit(1);
}
if(!fgets(buf,1024,fp))
printf("Nothing to read\n");
else
printf("Message Recieved : %s\n", buf);
fclose(fp);
sleep(1);
}
return 0;
}
I run the server first.
Secondly, on second terminal, when I run the client, I get the error :
Error while open call: No such device or address
What am I missing? I did man, and the parameters seem to be correct.
EDIT
Moving the open and close calls out of the while loop, did the job. But now if the client is started without starting the server, throws the following error :
Error while open call: No such device or address
The file /tmp/myFIFO exists on file-system from previous execution of the server, must be used by the client.
The main problem is opening and closing the file in a loop. This makes no sense. There is a very short time interval between opening and closing, and your client must hit it. It has little chance to do so. The "No such device or address" message happens exactly because the client misses the instant when the file is open. This is the main problem. Try moving open, fopen and fclose out of the server loop.
You also opening for reading but fopening for writing, but I suppose it's just a typo. This combination will not run. You need to change the mode of fopen to "r".
There are other, smaller issues.
You are not checking errors in the client. In your program the client will fail to open most of thee time, but sometimes open will succeed and write will fail.
It makes little sense to use stdio for the pipe in this program. read and write would do just fine.
Last but not least, sleep is an indication of a design issue. Indeed, in this program blocking I/O would make more sense. It's OK to use sleep if you just want to experiment with non-blocking I/O, but in real programs it should be avoided.
I have created a FIFO where I can do non-blocking writes into in this way:
// others, searching for a non-blocking FIFO-writer may copy this ;-)
mkfifo("/tmp/myfifo", S_IRWXU);
int fifo_fd = open("/tmp/myfifo", O_RDWR);
fcntl(fifo_fd, F_SETFL, fcntl(fifo_fd, F_GETFL) | O_NONBLOCK);
// and then in a loop:
LOGI("Writing into fifo.");
if (write(fifo_fd, data, count) < 0) {
LOGE("Failed to write into fifo: %s", strerror(errno));
}
The non-blocking write works perfect, but due to my logging I can see:
(..)
Writing into fifo.
Writing into fifo.
Writing into fifo.
Failed to write into fifo: Try again
Writing into fifo.
Failed to write into fifo: Try again
Writing into fifo.
Failed to write into fifo: Try again
After I created a reader like cat /tmp/myfifo > foo.out, the error goes away.
I thought the FIFO works like a ring-buffer and drops the first-written bytes when the buffer is full. But now I have learned that it blocks/prevents new bytes until the first bytes are read.
Does anyone know a simple other way or additional operation I can make so that the FIFO behaves like a ring-buffer?
From what I have been reading on The Open Group website on fcntl, open, read, and write, I get the impression that whether O_NONBLOCK is set on a file descriptor, and hence whether non-blocking I/O is used with the descriptor, should be a property of that file descriptor rather than the underlying file. Being a property of the file descriptor means, for example, that if I duplicate a file descriptor or open another descriptor to the same file, then I can use blocking I/O with one and non-blocking I/O with the other.
Experimenting with a FIFO, however, it appears that it is not possible to have a blocking I/O descriptor and non-blocking I/O descriptor to the FIFO simultaneously (so whether O_NONBLOCK is set is a property of the underlying file [the FIFO]):
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fds[2];
if (pipe(fds) == -1) {
fprintf(stderr, "`pipe` failed.\n");
return EXIT_FAILURE;
}
int fd0_dup = dup(fds[0]);
if (fd0_dup <= STDERR_FILENO) {
fprintf(stderr, "Failed to duplicate the read end\n");
return EXIT_FAILURE;
}
if (fds[0] == fd0_dup) {
fprintf(stderr, "`fds[0]` should not equal `fd0_dup`.\n");
return EXIT_FAILURE;
}
if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) {
fprintf(stderr, "`fds[0]` should not have `O_NONBLOCK` set.\n");
return EXIT_FAILURE;
}
if (fcntl(fd0_dup, F_SETFL, fcntl(fd0_dup, F_GETFL) | O_NONBLOCK) == -1) {
fprintf(stderr, "Failed to set `O_NONBLOCK` on `fd0_dup`\n");
return EXIT_FAILURE;
}
if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) {
fprintf(stderr, "`fds[0]` should still have `O_NONBLOCK` unset.\n");
return EXIT_FAILURE; // RETURNS HERE
}
char buf[1];
if (read(fd0_dup, buf, 1) != -1) {
fprintf(stderr, "Expected `read` on `fd0_dup` to fail immediately\n");
return EXIT_FAILURE;
}
else if (errno != EAGAIN) {
fprintf(stderr, "Expected `errno` to be `EAGAIN`\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
This leaves me thinking: is it ever possible to have a non-blocking I/O descriptor and blocking I/O descriptor to the same file and if so, does it depend on the type of file (regular file, FIFO, block special file, character special file, socket, etc.)?
O_NONBLOCK is a property of the open file description, not of the file descriptor, nor of the underlying file.
Yes, you could have separate file descriptors open for the same file, one of which is blocking and the other of which is non-blocking.
You need to distinguish between a FIFO (created using mkfifo()) and a pipe (created using pipe()).
Note that the blocking status is a property of the 'open file description', but in the simplest cases, there is a one-to-one mapping between file descriptors and open file descriptions. The open() function call creates a new open file description and a new file descriptor that refers to the open file description.
When you use dup(), you have two file descriptors sharing one open file description, and the properties belong to the open file description. The description of fcntl() says that F_SETFL affects the open file description associated with the file descriptor. Note that lseek() adjusts the file position of the open file description associated with the file descriptor - so it affects other file descriptors duplicated from the original one.
Removing the error handling from your code to reduce it, you have:
int fds[2];
pipe(fds);
int fd0_dup = dup(fds[0]);
fcntl(fd0_dup, F_SETFL, fcntl(fd0_dup, F_GETFL) | O_NONBLOCK);
Now both fd0_dup and fds[0] refer to the same open file description (because of the dup()), so the fcntl() operation affected both file descriptors.
if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) { ... }
Hence the observed behaviour here is required by POSIX.