Search code examples
clinuxnamed-pipes

How to read and write to a fifo (named pipe) in Linux?


I'm trying to open a fifo (named pipe) file, then write to it. I can't seem to make the server see what the "client" writes to it.

NOTE: I'm using terms like "client-server" for simplicity. I realize that they are actually just peers and that either side can close the pipe.

pipetest_server.c

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>

int keep_going = 1;

static void signal_handler(int signum)
{
    keep_going = 0;
}

int main()
{
    int fd;
    char *myfifo = "/tmp/mypipe";
    char *line;
    ssize_t amount_read;
    ssize_t len;
    FILE *f;

    signal(SIGINT, signal_handler);
    signal(SIGABRT, signal_handler);
    signal(SIGTERM, signal_handler);

    // make pipe
    mkfifo(myfifo, 0666);

    // open it
    fd = open(myfifo, O_RDWR);

    // make non-blocking
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

    // make a file descriptor for pipe
    f = fdopen(fd, "rw");

    while(keep_going)
    {
        amount_read = getline(&line, &len, f);
        if (amount_read > 0)
            printf(line);
    }

    printf("quitting...\n");

    close(fd);

    unlink(myfifo);

    return 0;
}

Then:

$ gcc pipetest_server.c
$ ./a.out

In another terminal:

$ ls -la /tmp/mypipe
prw-rw-r--. 1 myuser myuser 0 Nov 20 12:58 /tmp/mypipe

OK -- at this point, there is a pipe there. Now for the problems

1) Write to pipe with echo. Nothing happens

$ echo Hi >> /tmp/mypipe
<nothing comes out of the server>

2) Cat'ing the pipe, shows the data

$ cat /tmp/mypipe
Hi
<blinking cursor>

What's happening....

The server is never getting the data, but the pipe has the data in it.

3) When I close the server, the pipe goes away

<ctrl-C>
^Cquitting...

This tells me that the server did indeed create the pipe and has control over it.

4) I'd really like to create a another program to write to the pipe

Ultimately, I want to have another program that will write to this pipe and the two programs will communicate over it.

Note: There are other SO articles about this, but none that just get to the point.

UPDATE -- Working Code

Here is the final solution in case anyone comes after.

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>

static volatile sig_atomic_t keep_going = 1;

static void signal_handler(int signum)
{
    keep_going = 0;
}

int main()
{
    int fd;
    char *myfifo = "/tmp/mypipe";
    char *line;
    ssize_t amount_read;
    ssize_t len;
    FILE *f;

    signal(SIGINT, signal_handler);
    signal(SIGABRT, signal_handler);
    signal(SIGTERM, signal_handler);

    // make pipe
    mkfifo(myfifo, 0666);

    // open it
    fd = open(myfifo, O_RDWR);

    // make non-blocking
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

    // make a file descriptor for pipe
    f = fdopen(fd, "rw");

    while(keep_going)
    {
        amount_read = getline(&line, &len, f);
        if (amount_read > 0)
            printf(line);
        if (amount_read == -1)
        {
            clearerr(f);
        }
    }

    printf("quitting...\n");

    close(fd);

    unlink(myfifo);

    return 0;
}

Solution

  • The problem is that you've set the file descriptor to non-blocking, then opened a FILE * on it with fdopen. Stdio FILE streams do not understand non-blocking I/O, so on your first getline call the read will fail (with EWOULDBLOCK or EGAIN) and the FILE will go into error state, and getline will return -1. Once the FILE is in error state, every call will immediately return -1 without actually trying to read again.

    If you want to actually use non-blocking mode, you need to call clearerr(f) after getline returns -1. You should also be checking errno, to be sure the error is EWOULDBLOCK or EAGAIN -- if there's a real error it will be something else.