From 1e61ce4e124c9a60be9650055161d230c0390b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Sat, 9 Feb 2019 12:43:44 +0100 Subject: [PATCH] Improve error reporting when spawning child process fails Suppress PHP warnings on error and report any error message as part of exception messages as expected. --- src/Process.php | 5 ++-- tests/AbstractProcessTest.php | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/Process.php b/src/Process.php index 08cf99d..7ee72b8 100644 --- a/src/Process.php +++ b/src/Process.php @@ -189,10 +189,11 @@ public function start(LoopInterface $loop, $interval = 0.1) $options['suppress_errors'] = true; } - $this->process = \proc_open($cmd, $fdSpec, $pipes, $this->cwd, $this->env, $options); + $this->process = @\proc_open($cmd, $fdSpec, $pipes, $this->cwd, $this->env, $options); if (!\is_resource($this->process)) { - throw new \RuntimeException('Unable to launch a new process.'); + $error = \error_get_last(); + throw new \RuntimeException('Unable to launch a new process: ' . $error['message']); } // count open process pipes and await close event for each to drain buffers before detecting exit diff --git a/tests/AbstractProcessTest.php b/tests/AbstractProcessTest.php index 62f4c94..20b919f 100644 --- a/tests/AbstractProcessTest.php +++ b/tests/AbstractProcessTest.php @@ -84,6 +84,56 @@ public function testStartWithCustomPipesWillAssignPipes() $this->assertInstanceOf('React\Stream\WritableStreamInterface', $process->pipes[3]); } + /** + * @expectedException RuntimeException + * @expectedExceptionMessage No such file or directory + */ + public function testStartWithInvalidFileDescriptorPathWillThrow() + { + $fds = array( + 4 => array('file', '/dev/does-not-exist', 'r') + ); + + $process = new Process('exit 0', null, null, $fds); + $process->start($this->createLoop()); + } + + public function testStartWithExcessiveNumberOfFileDescriptorsWillThrow() + { + if (PHP_VERSION_ID < 70000) { + $this->markTestSkipped('PHP 7+ only, causes memory overflow on legacy PHP 5'); + } + + $ulimit = exec('ulimit -n 2>&1'); + if ($ulimit < 1) { + $this->markTestSkipped('Unable to determine limit of open files (ulimit not available?)'); + } + + $loop = $this->createLoop(); + + // create 70% (usually ~700) dummy file handles in this parent dummy + $limit = (int)($ulimit * 0.7); + $fds = array(); + for ($i = 0; $i < $limit; ++$i) { + $fds[$i] = fopen('/dev/null', 'r'); + } + + // try to create child process with another ~700 dummy file handles + $new = array_fill(0, $limit, array('file', '/dev/null', 'r')); + $process = new Process('ping example.com', null, null, $new); + + try { + $process->start($loop); + + $this->fail('Did not expect to reach this point'); + } catch (\RuntimeException $e) { + // clear dummy files handles to make some room again (avoid fatal errors for autoloader) + $fds = array(); + + $this->assertContains('Too many open files', $e->getMessage()); + } + } + public function testIsRunning() { if (DIRECTORY_SEPARATOR === '\\') {