Skip to content

Commit

Permalink
Capture signals and wait until child process exits to also exit, fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Seldaek authored and emahorvat52 committed Jan 18, 2023
1 parent 518193b commit a7f4cd3
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 7 deletions.
11 changes: 9 additions & 2 deletions src/Composer/Console/Application.php
Expand Up @@ -17,6 +17,7 @@
use Composer\Util\Platform;
use Composer\Util\Silencer;
use LogicException;
use Seld\Signal\SignalHandler;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\Console\Helper\HelperSet;
Expand Down Expand Up @@ -93,7 +94,15 @@ public function __construct()
date_default_timezone_set(Silencer::call('date_default_timezone_get'));
}

$this->io = new NullIO();

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;

register_shutdown_function(static function (): void {
Expand All @@ -107,8 +116,6 @@ public function __construct()
});
}

$this->io = new NullIO();

$this->initialWorkingDirectory = getcwd();

parent::__construct('Composer', Composer::getVersion());
Expand Down
2 changes: 2 additions & 0 deletions src/Composer/EventDispatcher/ScriptExecutionException.php
Expand Up @@ -13,6 +13,8 @@
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>
*/
class ScriptExecutionException extends \RuntimeException
Expand Down
27 changes: 22 additions & 5 deletions src/Composer/Util/ProcessExecutor.php
Expand Up @@ -14,6 +14,8 @@

use Composer\IO\IOInterface;
use Composer\Pcre\Preg;
use Seld\Signal\SignalHandler;
use Symfony\Component\Process\Exception\ProcessSignaledException;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\RuntimeException;
use React\Promise\Promise;
Expand Down Expand Up @@ -125,13 +127,28 @@ private function doExecute($command, ?string $cwd, bool $tty, &$output = null):
$this->outputHandler($type, $buffer);
};

$process->run($callback);
$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);
}
});

if ($this->captureOutput && !is_callable($output)) {
$output = $process->getOutput();
}
try {
$process->run($callback);

$this->errorOutput = $process->getErrorOutput();
if ($this->captureOutput && !is_callable($output)) {
$output = $process->getOutput();
}

$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();
}
Expand Down

0 comments on commit a7f4cd3

Please sign in to comment.