How to check if the pipe is opend before writing?
The correct way is to test the return code of write
and then also check errno
:
if (write(pipe, msg, strlen(msg)) == -1) {
if (errno == EPIPE) {
/* Closed pipe. */
}
}
But wait: writing to a closed pipe no only returns -1 with errno=EPIPE
, it also sends a SIGPIPE
signal which terminates your process:
EPIPE fd is connected to a pipe or socket whose reading end is
closed. When this happens the writing process will also receive a
SIGPIPE signal.
So before that testing works you also need to ignore SIGPIPE
:
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
perror("signal");
Check whether named pipe/FIFO is open for writing
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
How to see if a pipe is empty
Your logic is wrong in that read
will not return 0 when it runs out of characters; instead, it will block until it receives more, unless you put the file in non-blocking mode, but then it will return -1 and set errno
to EWOULDBLOCK
or EAGAIN
rather than returning 0. The only time read
can ever return 0 is when the size argument was 0 or end-of-file has been reached. And, for pipes, end-of-file means the writing end of the pipe has been closed; end-of-file status does not occur just because there's not any input available yet.
With that said, the simplest way to check is:
if (poll(&(struct pollfd){ .fd = fd, .events = POLLIN }, 1, 0)==1) {
/* data available */
}
but unless you're using nonblocking mode, you'll need to make this check before every single read operation. Passing a larger buffer to read
rather than doing it a byte-at-a-time would eliminate most of the cost of checking.
How to test that no data is written to an NSPipe
As outlined in the comments, I've found a separate solution which writes arbitrary data to the pipe, executes the log (which may or may not result in more data being written to the pipe), and then retrieves the data from the pipe's availableData
.
If the availableData
's length is equal to that of the arbitrary data, then it is clear that the log has not been written to the pipe. Otherwise if the length is greater than the data that the test has written, the log has indeed been executed and written its data to the pipe.
let logger = Logger()
let pipe = NSPipe()
logger.pipe = pipe
logger.enabled = false
logger.useCurrentThread = true // The logs generally use a background thread to prevent interrupting the main queue. In this test, the current thread must be used in order for it to be synchronous.
let writtenData = "written data".dataUsingEncoding(NSUTF8StringEncoding)!
logger.pipe!.fileHandleForWriting.writeData(writtenData)
logger.debug("Test message")
XCTAssertEqual(logger.pipe!.fileHandleForReading.availableData.length, writtenData.length)
Related Topics
(Delphi Linux) Ld-Linux.Exe: Error: Cannot Find -Lgcc_S
Why Do We Need to Call Poll_Wait in Poll
Xkb: How to Convert a Keycode to Keysym
Low-Overhead Way to Access the Memory Space of a Traced Process
Replace Text Based on a Dictionary
Linux Iterate Over Files in Directory
Add Suffix to Each Line with Shell Script
How to Install Haskell Platform on Linux Debian Wheezy
Triggering Autokey Script via Mouse Button - How To
How to Set Runpath of a Binary
How to Uniquely Identify an Usb-Device
Configuring Oracle Listener to Listen Externally
How to Compare 2 Lists of Ranges in Bash
Linux Cant Find Dynamically Linked Applications
Linux Diff Get Only Line Number in the Output
Why Does Unitywebrequest Return Unkown Error When I Do a Get Request on Linux