# preventing FIFO from EOF



## rihad (Dec 12, 2008)

$ mkfifo /var/tmp/foo
$ buffer -i /var/tmp/foo                    # misc/buffer
# in another console:
$ echo hi > /var/tmp/foo

buffer prints hi and exits. I want it to keep reading and printing indefinitely.

Further experimentation revealed that I need two writers: one dummy writer that just keeps /var/tmp/foo open for writing, and the other doing the "real work". This way buffer wouldn't exit. But how to emulate the dummy writer? It itself needs to block on something to keep /var/tmp/foo open. Any clean way to do this in shell? Maybe the solution is quite simple but isn't at the tip of my tongue.

Thanks.


----------



## graudeejs (Dec 12, 2008)

i have no idea what i will say now (it might even be stupid, don't blaim, but here are my 2 cents)

filter EOFs and cat the rest


----------



## rihad (Dec 12, 2008)

Huh? EOF's are logical, you can't touch them 

Strangely enough, something as stupid as this does the trick:
on tty1:
$ sh < /dev/stdout > /var/tmp/kick 2>/dev/null
on tty2:
$ buffer -i /var/tmp/foo
on tty3:
$ echo hi > /var/tmp/foo
$
now buffer blocks forever irregardless of whether echo exits as soon as it writes "hi" or not.

I'm not sure what reading from stdout does or why and how it works Unix-wise, but it does work.


----------



## SirDice (Dec 12, 2008)

Unix treats everything as a file, including FIFOs.

An *echo hi > /var/tmp/foo* opens /var/tmp/foo, writes 'hi' to it then closes /var/tmp/foo. That last action triggers an EOF.


----------



## rihad (Dec 13, 2008)

rihad said:
			
		

> Strangely enough, something as stupid as this does the trick:
> on tty1:
> $ sh < /dev/stdout > /var/tmp/kick 2>/dev/null


Nooo  this seems to work from console but doesn't in /usr/local/etc/rc.d/file.sh. 

I need a lovely Unix device (like /dev/zero or /dev/null) designed to always block on input, so I can substitute /dev/stdout above. Possible?


----------



## rihad (Dec 13, 2008)

Finally:

tail -f /var/tmp/foo | buffer

Now buffer never exits.


----------



## dap (Dec 14, 2008)

IMO it would be wore straightforward to write a program. Here is a small example:


```
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

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

#define FIFO_NAME "/var/tmp/foo"

int main (void)
{
  umask(0);

  if ( mkfifo(FIFO_NAME, 0666) == -1 && errno != EEXIST )
  {
    perror("mkfifo()");
  }
  else
  {
    int fd = open(FIFO_NAME, O_RDONLY);

    if (fd == -1)
    {
      perror("open()");
    }
    else
    {
      char buffer[32];
      int error = 0;

      while (error == 0)
      {
        int readRet = read(fd, buffer, sizeof buffer);

        if (readRet == -1)
        {
          perror("read()");
          error = 1;
        }
        /* there is no more reader; close the FIFO and reopen it to wait for */
        /* a new one */
        else if (readRet == 0)
        {
          close(fd);
          fd = open(FIFO_NAME, O_RDONLY);
        }
        else
        {
          buffer[sizeof buffer - 1] = '\0';
          printf("Received : [%s]\n", buffer);
        }
      }

      close(fd);
    }
  }

  return EXIT_FAILURE;
}
```

I have removed some of the error control to make it easier to understand.


----------



## jy (Jan 2, 2013)

I have the same problem but in code, but the above solution has a serious race condition.


```
/* there is no more reader; close the FIFO and reopen it to wait for */
        /* a new one */
        else if (readRet == 0)
        {
          close(fd);
          fd = open(FIFO_NAME, O_RDONLY);
        }
```


that section, what if someone connects after the read returns and before you close the fd? There needs to be a method to reset the EOF flag without risking anyone connecting. Without that, it turns into a busy loop, but at least no data is lost..

thoughts?


----------



## jy (Jan 2, 2013)

Hmm. I am not clear exactly where the buffering happens in the FIFO. With the above in mind, I implemented it with a small change - open a new copy of the file first before closing the old one, so that a client never sees a one-ended pipe. This seems to work under stress testing with 10 clients interleaved..

So with this in mind I  need to edit my prev message: I think it is probably OK, as the FIFO should continue to internally buffer in the case where the read side of the FIFO closes while the client writes.


----------



## rihad (Jan 3, 2013)

@jy, I don't think FIFO buffers anything, for that you'd need an intermediary reading/buffering/writing stuff (like misc/buffer from ports I used for shell scripting).

Experimentation showed that attempting to open FIFO for writing blocks until there's a reader. As soon as there is one, open returns. If the reader suddenly goes away, writes fail. Here's my testbed:

In one Bash console run the writer:
`$ mkfifo /tmp/fifo`
`$ ( echo beforehi >&2; echo hi; echo wrotehi >&2; sleep 5; echo fi; echo wrotefi >&2 ) > /tmp/fifo`
In another console run the reader:
`$ cat /tmp/fifo`
Try first invoking the reader, then the writer, and vice versa. Try killing the reader in the process.


----------

