11

I've created a named pipe for some other process to write to and want to check that the other process started correctly, but don't know its PID. The context is running a command in screen, making sure the command started correctly. I was hoping this might work:

mkfifo /tmp/foo
echo hello > /tmp/foo &
lsof /tmp/foo

Sadly, lsof does not report echo. inotifywait might be another option, but isn't always installed and I really want to poll just once, rather than block until some event.

Is there any way to check if a named pipe is open for writing? Even open in general?


UPDATE:

Once both ends are connected lsof seems to work. This actually solves my problem, but for the sake of the question I'd be interested to know if it's possible to detect the initial redirection to the named pipe without a reader.

> mkfifo /tmp/foo
> yes > /tmp/foo &
> lsof /tmp/foo
> cat /tmp/foo > /dev/null &
> lsof /tmp/foo
COMMAND   PID     USER   FD   TYPE DEVICE SIZE/OFF     NODE     NAME
yes     16915     user    1w  FIFO   8,18      0t0 16660270 /tmp/foo
cat     16950     user    3r  FIFO   8,18      0t0 16660270 /tmp/foo
Community
  • 1
  • 1
jozxyqk
  • 16,424
  • 12
  • 91
  • 180
  • When you say "open for writing", do you mean that there is data available to read? What, to you, is a FIFO that is not open for writing? – Fred Feb 06 '17 at 19:19
  • 1
    Not the answer you're looking for, but you could write a .pid file – teppic Feb 06 '17 at 19:23
  • @Fred Maybe "open for writing" is the wrong term if you think the question doesn't make sense. I'd ideally like to list processes that are redirected to that named pipe. I.e. after `mkfifo` and nothing else, the list should be empty. Then after `yes > /tmp/foo` I should see the PID of `yes` in the list. – jozxyqk Feb 06 '17 at 19:31
  • @What I would like to clarify is this : are you trying to determine if the writer process is actually writing, or are you trying to determine if the reader is actually reading? – Fred Feb 06 '17 at 19:33
  • I think @teppic's right that you might be trying to solve the wrong problem. It's an interesting problem, though. – Harvey Feb 06 '17 at 19:33

2 Answers2

5

Update 2: After playing with inotify-tools, there doesn't seem to be a way to get a notification that a named pipe has been opened for writing and is blocking. This is probably why lsof doesn't show the pipe until it has a reader and a writer.

Update: After researching named pipes, I don't believe that there is any method that will work with named pipes by themselves. Reasoning:

  • there is no way to limit the number of writers to a named pipe (without resorting to locking)
  • all writers block if there is no reader
  • no writers block if there is a reader (presumably as long as the kernel buffers aren't full)

You could try writing nothing to the pipe with a short timeout. If the timeout expires, then the write blocked indicating that someone has already opened the pipe for writing.

Note: As pointed out in the comments, if a reader exists and presumably is fast enough, our test write will not block and the test essentially fails. Comment out the cat line below to test this.

#!/bin/bash

is_named_pipe_already_opened_for_writing() {
    local named_pipe="$1"
    # Make sure it's a named pipe
    if ! [ -p "$named_pipe" ]; then
        return 1
    fi
    # Try to write zero bytes in the background
    echo -n > "$named_pipe" &
    pid=$!
    # Wait a short amount of time
    sleep 0.1
    # Kill the background process. If kill succeeds, then
    # the write was blocked indicating that someone
    # else is already writing to the named pipe.
    kill $pid 2>/dev/null
}

PIPE=/tmp/foo

# Ignore any bash messages from killing below
trap : TERM

mkfifo $PIPE
# a writer
yes > $PIPE &
# a reader
cat $PIPE >/dev/null &

if is_named_pipe_already_opened_for_writing "$PIPE"; then
    echo "$PIPE is already being written to by another process"
else
    echo "$PIPE is NOT being written to by another process"
fi

jobs -pr | kill 2>/dev/null
rm -f $PIPE
Harvey
  • 5,703
  • 1
  • 32
  • 41
  • Yes, this works provided there is nothing reading from the pipe. – jozxyqk Feb 06 '17 at 20:25
  • Relying an a specific sleep duration (which, if I understand correctly, is supposed to give enough time for the writ test background process to terminate) seems a bit fragile. Wouldn't that risk having unexpected results under certains situations (e.g. high CPU or IO load)? – Fred Feb 06 '17 at 22:08
  • Maybe I'm doing something wrong because I thought I remembered this working. It seems `echo` always blocks unless there's a reader, even on a brand new pipe. – jozxyqk Feb 06 '17 at 23:32
  • @Fred: it might have issues, but under reasonable situations, a suitable amount of time can be chosen that both works for the test and doesn't take too long. – Harvey Feb 07 '17 at 12:58
  • @jozxyqk: All writers block unless there's a reader. See my updates to the answer. – Harvey Feb 07 '17 at 12:59
  • @Harvey Only the OP can tell what kind of issues would result from relying on "reasonable assumptions about process scheduling", and maybe it is OK in that specific scenario, but if we are talking mission critical stuff or risks of data loss, I would say stay away from "reasonable", and stick with "guaranteed". – Fred Feb 07 '17 at 13:04
  • @Fred, no one (paying attention) would be using this for "mission critical" applications because it's not deterministic ("guaranteed"), so "reasonable" is fine. – Harvey Feb 07 '17 at 13:31
  • probably better to use one of these answers instead: https://unix.stackexchange.com/questions/522929/check-if-named-pipe-is-open-for-reading/522940?noredirect=1#comment966889_522940 – Alexander Mills Jun 05 '19 at 00:28
0

you need two pipes one for each directions: one is use to wait for the ready for new data signal, another just for the data: in my case process to files, line by line:

mkfifo r w;

cat file1 | while read l; do echo "$l" >w; read <r; done &
cat file2 | while read ln; do if read l <w; then echo "$ln"; echo "$l"; fi; echo 1>r;  done
bigg
  • 1