I am writing a wee wrapper (in PHP 7.0.33) around a complex binary which takes its input from a named file. As this will be processing secrets, I don't want to commit the data to the filesystem, hence using a FIFO rather than conventional file. The binary will happily read its data from a FIFO (tested using 2 shell sessions - in one I created the fifo and started the binary, in the second I catted a file into the fifo).
However in PHP the call to fopen() blocks, regardless if I specify w, a or c
if (posix_mkfifo("myfifo", 0600)) {
$writer=fopen("myfifo", 'w'); // this blocks
`binary myfifo`;
fputs($writer, $mydata);
}
While I would expect writes to block if nothing is reading the data, I did not expect fopen() to block.
It does appear to work (execution continues, and the binary is started) with "w+" however the binary fails with
QIODevice::read (QFile, "filename"): device not open
To investigate further, I wrote a simple replacement for the binary. Again this works when I cat a file into the FIFO:
$in='';
$fh=fopen($argv[1],'r');
if (is_resource($fh)) {
print "File opened\n";
while (!feof($fh)) {
$in.=fgets($fh);
}
} else {
print "failed to open file\n";
}
file_put_contents("output", $in);
but when I write to the FIFO from the PHP code....
fopen(import): failed to open stream: No such file or directory in ...
By default, opening a FIFO will block until there is at least one reader and writer. The rationale for this is that the kernel has no place to stash the pipe data if no process is there to consume it. man page for fifo:
... the FIFO special file has no contents on the file system, the file system entry merely serves as a reference point so that processes can access the pipe using a name in the file system.
The kernel maintains exactly one pipe object for each FIFO special file that is opened by at least one process. The FIFO must be opened on both ends (reading and writing) before data can be passed. Normally, opening the FIFO blocks until the other end is opened also.
You can bypass this behaviour though. One way is like you've done - by opening the read and write end yourself. The other is to set O_NONBLOCK
flag when opening the file (you can set it back to block afterwards). AFAIK you can't do that with fopen. Example with dio
library:
<?php
echo "Opening\n";
$writer = dio_open("myfifo", O_CREAT | O_WRONLY | O_NONBLOCK) or die("Could not create FIFO\n");
echo "Open. Writing\n";
dio_write($writer, "DATA");
echo "Done\n";
The caveat with doing this is, if there is no reader, the process above will write the data, then exit immediately and then the data is lost forever.