1
0
Fork 0

Capture signals and wait until child process exits to also exit, fixes #6059

pull/10958/head
Jordi Boggiano 2022-07-20 22:08:44 +02:00
parent 1a4f2174e4
commit 07645b9895
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
3 changed files with 33 additions and 7 deletions

View File

@ -17,6 +17,7 @@ use Composer\Util\Filesystem;
use Composer\Util\Platform; use Composer\Util\Platform;
use Composer\Util\Silencer; use Composer\Util\Silencer;
use LogicException; use LogicException;
use Seld\Signal\SignalHandler;
use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Helper\HelperSet; use Symfony\Component\Console\Helper\HelperSet;
@ -93,7 +94,15 @@ class Application extends BaseApplication
date_default_timezone_set(Silencer::call('date_default_timezone_get')); date_default_timezone_set(Silencer::call('date_default_timezone_get'));
} }
$this->io = new NullIO();
if (!$shutdownRegistered) { if (!$shutdownRegistered) {
$signalHandler = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGTERM, SignalHandler::SIGHUP], function (string $signal, SignalHandler $handler) {
$this->io->writeError('Received '.$signal.', aborting', true, IOInterface::DEBUG);
$handler->exitWithLastSignal();
});
$shutdownRegistered = true; $shutdownRegistered = true;
register_shutdown_function(static function (): void { register_shutdown_function(static function (): void {
@ -107,8 +116,6 @@ class Application extends BaseApplication
}); });
} }
$this->io = new NullIO();
$this->initialWorkingDirectory = getcwd(); $this->initialWorkingDirectory = getcwd();
parent::__construct('Composer', Composer::getVersion()); parent::__construct('Composer', Composer::getVersion());

View File

@ -13,6 +13,8 @@
namespace Composer\EventDispatcher; namespace Composer\EventDispatcher;
/** /**
* Thrown when a script running an external process exits with a non-0 status code
*
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class ScriptExecutionException extends \RuntimeException class ScriptExecutionException extends \RuntimeException

View File

@ -14,6 +14,8 @@ namespace Composer\Util;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Pcre\Preg; use Composer\Pcre\Preg;
use Seld\Signal\SignalHandler;
use Symfony\Component\Process\Exception\ProcessSignaledException;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\RuntimeException; use Symfony\Component\Process\Exception\RuntimeException;
use React\Promise\Promise; use React\Promise\Promise;
@ -125,6 +127,13 @@ class ProcessExecutor
$this->outputHandler($type, $buffer); $this->outputHandler($type, $buffer);
}; };
$signalHandler = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGTERM, SignalHandler::SIGHUP], function (string $signal) {
if ($this->io !== null) {
$this->io->writeError('Received '.$signal.', aborting when child process is done', true, IOInterface::DEBUG);
}
});
try {
$process->run($callback); $process->run($callback);
if ($this->captureOutput && !is_callable($output)) { if ($this->captureOutput && !is_callable($output)) {
@ -132,6 +141,14 @@ class ProcessExecutor
} }
$this->errorOutput = $process->getErrorOutput(); $this->errorOutput = $process->getErrorOutput();
} catch (ProcessSignaledException $e) {
if ($signalHandler->isTriggered()) {
// exiting as we were signaled and the child process exited too due to the signal
$signalHandler->exitWithLastSignal();
}
} finally {
$signalHandler->unregister();
}
return $process->getExitCode(); return $process->getExitCode();
} }