Skip to content

Commit c94151d

Browse files
committed
feat(process): allow to ignore signals when executing a process
1 parent fdedd3b commit c94151d

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

src/Symfony/Component/Process/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.1
5+
---
6+
7+
* Add `Process::setIgnoredSignals()` to disable signal propagation to the child process
8+
49
6.4
510
---
611

src/Symfony/Component/Process/Process.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class Process implements \IteratorAggregate
7777
private bool $tty = false;
7878
private bool $pty;
7979
private array $options = ['suppress_errors' => true, 'bypass_shell' => true];
80+
private array $ignoredSignals = [];
8081

8182
private WindowsPipes|UnixPipes $processPipes;
8283

@@ -346,9 +347,23 @@ public function start(?callable $callback = null, array $env = []): void
346347

347348
return true;
348349
});
350+
351+
$oldMask = [];
352+
353+
if (\function_exists('pcntl_sigprocmask')) {
354+
// we block signals we want to ignore, as proc_open will use fork / posix_spawn which will copy the signal mask this allow to block
355+
// signals in the child process
356+
pcntl_sigprocmask(\SIG_BLOCK, $this->ignoredSignals, $oldMask);
357+
}
358+
349359
try {
350360
$process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options);
351361
} finally {
362+
if (\function_exists('pcntl_sigprocmask')) {
363+
// we restore the signal mask here to avoid any side effects
364+
pcntl_sigprocmask(\SIG_SETMASK, $oldMask);
365+
}
366+
352367
restore_error_handler();
353368
}
354369

@@ -1206,6 +1221,20 @@ public function setOptions(array $options): void
12061221
}
12071222
}
12081223

1224+
/**
1225+
* Define a list of posix signals that will not be propagated to the process.
1226+
*
1227+
* @param list<\SIG*> $signals
1228+
*/
1229+
public function setIgnoredSignals(array $signals): void
1230+
{
1231+
if ($this->isRunning()) {
1232+
throw new RuntimeException('Setting ignored signals while the process is running is not possible.');
1233+
}
1234+
1235+
$this->ignoredSignals = $signals;
1236+
}
1237+
12091238
/**
12101239
* Returns whether TTY is supported on the current operating system.
12111240
*/
@@ -1455,6 +1484,11 @@ private function resetProcessData(): void
14551484
*/
14561485
private function doSignal(int $signal, bool $throwException): bool
14571486
{
1487+
// Signal seems to be send when sigchild is enable, this allow blocking the signal correctly in this case
1488+
if ($this->isSigchildEnabled() && \in_array($signal, $this->ignoredSignals)) {
1489+
return false;
1490+
}
1491+
14581492
if (null === $pid = $this->getPid()) {
14591493
if ($throwException) {
14601494
throw new LogicException('Cannot send signal on a non running process.');

src/Symfony/Component/Process/Tests/ProcessTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,6 +1663,36 @@ public function testNotTerminableInputPipe()
16631663
$this->assertFalse($process->isRunning());
16641664
}
16651665

1666+
public function testIgnoringSignal()
1667+
{
1668+
if (!\function_exists('pcntl_signal')) {
1669+
$this->markTestSkipped('pnctl extension is required.');
1670+
}
1671+
1672+
$process = $this->getProcess('sleep 10');
1673+
$process->setIgnoredSignals([\SIGTERM]);
1674+
1675+
$process->start();
1676+
$process->stop(timeout: 0.2);
1677+
1678+
$this->assertNotSame(\SIGTERM, $process->getTermSignal());
1679+
}
1680+
1681+
// This test ensure that the previous test is reliable, in case of the sleep command ignoring the SIGTERM signal
1682+
public function testNotIgnoringSignal()
1683+
{
1684+
if (!\function_exists('pcntl_signal')) {
1685+
$this->markTestSkipped('pnctl extension is required.');
1686+
}
1687+
1688+
$process = $this->getProcess('sleep 10');
1689+
1690+
$process->start();
1691+
$process->stop(timeout: 0.2);
1692+
1693+
$this->assertSame(\SIGTERM, $process->getTermSignal());
1694+
}
1695+
16661696
private function getProcess(string|array $commandline, ?string $cwd = null, ?array $env = null, mixed $input = null, ?int $timeout = 60): Process
16671697
{
16681698
if (\is_string($commandline)) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy