From 5448fe2165df0d37c1042779a16346050dd09f8d Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 1 May 2020 06:59:05 -0700 Subject: [PATCH] When recent PHP raises a "broken pipe" error in ExecFuture, treat it as a blocked stdin Summary: Ref T13528. If we start a subprocess that immediately exits and then write to it, we can get a broken pipe error. Recent versions of PHP appear to raise this as an actual warning, and recent changes upgrade the warning to a runtime exception. I can't find any way to tell if the RuntimeException is a broken pipe or something else, except by examining the text of the error string. At least for now, treat this like a "blocked pipe" condition. Since the subprocess has exited and the bytes didn't write, this should generally be reasonable. Test Plan: - Viewed a file in Paste with an extension that Pygments does not have a lexer for. - This causes Pygments to exit immediately with an "unrecognized lexer" error. This closes the pipe, and the next write will fail with a broken pipe error. - Before patch: fatal on broken pipe. - After patch: clean resolution of the future and error condition. Maniphest Tasks: T13528 Differential Revision: https://secure.phabricator.com/D21199 --- src/future/exec/ExecFuture.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/future/exec/ExecFuture.php b/src/future/exec/ExecFuture.php index 48911930..0264d4d3 100644 --- a/src/future/exec/ExecFuture.php +++ b/src/future/exec/ExecFuture.php @@ -713,7 +713,17 @@ final class ExecFuture extends PhutilExecutableFuture { while (isset($this->stdin) && $this->stdin->getByteLength()) { $write_segment = $this->stdin->getAnyPrefix(); - $bytes = fwrite($stdin, $write_segment); + try { + $bytes = fwrite($stdin, $write_segment); + } catch (RuntimeException $ex) { + // If the subprocess has exited, we may get a broken pipe error here + // in recent versions of PHP. There does not seem to be any way to + // get the actual error code other than reading the exception string. + + // For now, treat this as if writes are blocked. + break; + } + if ($bytes === false) { throw new Exception(pht('Unable to write to stdin!')); } else if ($bytes) {