Skip to content

Commit

Permalink
Improve platform support by checking all available FDs as fallback
Browse files Browse the repository at this point in the history
If we can not read from /dev/fd (which is available on Linux, Mac and
many others), we otherwise try temporarily duplicating file descriptors
in the range 0-1024 (FD_SETSIZE) to see which one is currently in use.
This is known to work on more exotic platforms and also inside
chroot environments without /dev/fd. Causes many syscalls, but still
rather fast.
  • Loading branch information
clue committed Jan 19, 2019
1 parent 27ccbdd commit e7512e6
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 5 deletions.
18 changes: 16 additions & 2 deletions src/Io/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,24 @@
*/
function fds($path = '/dev/fd')
{
// try to get list of all open FDs (Linux/Mac and others) or simply assume range 0-1024 (FD_SETSIZE)
// Try to get list of all open FDs (Linux/Mac and others)
$fds = @\scandir($path);

return $fds !== false ? $fds : \range(0, 1024);
// Otherwise try temporarily duplicating file descriptors in the range 0-1024 (FD_SETSIZE).
// This is known to work on more exotic platforms and also inside chroot
// environments without /dev/fd. Causes many syscalls, but still rather fast.
if ($fds === false) {
$fds = array();
for ($i = 0; $i <= 1024; ++$i) {
$copy = @\fopen('php://fd/' . $i, 'r');
if ($copy !== false) {
$fds[] = $i;
\fclose($copy);
}
}
}

return $fds;
}

/**
Expand Down
20 changes: 17 additions & 3 deletions tests/Io/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,29 @@ public function testFdsWithInvalidPathReturnsArray()
$this->assertInternalType('array', $fds);
}

public function testFdsWithInvalidPathReturnsSubsetOfFdsFromDevFd()
{
if (@scandir('/dev/fd') === false) {
$this->markTestSkipped('Unable to read /dev/fd');
}

$fds = Io\fds();
$second = Io\fds('/dev/null');

foreach ($second as $one) {
$this->assertContains($one, $fds);
}
}

public function testProcessWithoutFdsReturnsProcessWithoutClosingDefaultHandles()
{
$process = Io\processWithoutFds('sleep 10');

$this->assertInstanceOf('React\ChildProcess\Process', $process);

$this->assertNotContains('0>&-', $process->getCommand());
$this->assertNotContains('1>&-', $process->getCommand());
$this->assertNotContains('2>&-', $process->getCommand());
$this->assertNotContains(' 0>&-', $process->getCommand());
$this->assertNotContains(' 1>&-', $process->getCommand());
$this->assertNotContains(' 2>&-', $process->getCommand());
}

public function testProcessWithoutFdsReturnsProcessWithOriginalCommandPartOfActualCommandWhenDescriptorsNeedToBeClosed()
Expand Down

0 comments on commit e7512e6

Please sign in to comment.