Skip to content

implementing SignalableCommandInterface in standalone symfony/console might prevent SIGINT/SIGTERM #57342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jtopenpetition opened this issue Jun 7, 2024 · 3 comments

Comments

@jtopenpetition
Copy link

jtopenpetition commented Jun 7, 2024

Symfony version(s) affected

6.4.8 (and probably 7.x, 6.x and before?)

Description

the documentation for signals in console claims:

Symfony doesn't handle any signal received by the command (not even SIGKILL,
SIGTERM, etc). This behavior is intended, as it gives you the flexibility to
handle all signals e.g. to do some tasks before terminating the command.
https://github.com/symfony/symfony-docs/blob/f3056736e5d9a4a8d551467a4d3e8272787c1425/components/console/events.rst?plain=1#L245-L247

However, this is not true for (at least) the standalone usage, for SIGINT and SIGTERM, when no event dispatcher is set. In these cases a signal handler is installed that does apparently prevent the default behaviour.

As is, unless also subscribing to SIGINT/SIGTERM with SignableCommandInterface and exiting in those cases, the process will keep running, since the default behaviour is prevented due to some registered handlers that don't provide the default behaviour.

Since the code hasn't changed much, I suppose this weird behaviour still exists for symfony 7+

How to reproduce

standalone usage of symfony/console with a command implementing the SignalableCommandInterface and subscribing for example to just SIGTERM. handleSignal can do whatever.

	public function getSubscribedSignals(): array
	{
		return [\SIGTERM];
	}

SIGINT in a standard shell (Ctrl-c) ceases to work - presuming stty is available (Terminal::hasSttyAvailable() is true), due to a handler being registered:

if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
foreach ([\SIGINT, \SIGTERM] as $signal) {
$this->signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode));
}
}

this handler obviously doesn't affect anything really and is installed if there are subscribed signals by the command but no event dispatcher is set on the application.

Possible Solution

Since it's unclear, what those handlers are for, it's unclear how to fix this in code.

The obvious workaround of subscribing to those two signals as well is error-prone (since you have to remember). Adding a warning to the documentation might also help if this (supposed) bug is not going to be fixed.

@jtopenpetition jtopenpetition changed the title implementing SignalableCommandInterface in standalone console might prevent sigint/sigterm implementing SignalableCommandInterface in standalone console might prevent SIGINT/SIGTERM Jun 7, 2024
@jtopenpetition jtopenpetition changed the title implementing SignalableCommandInterface in standalone console might prevent SIGINT/SIGTERM implementing SignalableCommandInterface in standalone symfony/console might prevent SIGINT/SIGTERM Jun 7, 2024
@xabbuh xabbuh added the Console label Jun 7, 2024
@carsonbot
Copy link

Hey, thanks for your report!
There has not been a lot of activity here for a while. Is this bug still relevant? Have you managed to find a workaround?

@jtopenpetition
Copy link
Author

Is this bug still relevant? Have you managed to find a workaround?

It's still relevant and alive. The workaround is to register all signals, that are being handled by default, which now also includes SIGQUIT:

if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
foreach ([\SIGINT, \SIGQUIT, \SIGTERM] as $signal) {
$signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode));
}
}

@carsonbot carsonbot removed the Stalled label Dec 9, 2024
@lenar
Copy link
Contributor

lenar commented Apr 30, 2025

Probably related. Freshly created project, with freshly created console command that's blocking on some socket operation:

protected function execute(InputInterface $input, OutputInterface $output): int
{
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    socket_connect($socket, 'www.google.com', 80);
    socket_read($socket, 1024);
}

CTRL+C does not work, cannot abort the command.

What makes CTRL+C to work is to change pcntl_signal($signal, $this->handle(...)); to pcntl_signal($signal, $this->handle(...), restart_syscalls: false); in Symfony\Component\Console\SignalRegistry\SignalRegistry::register().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants