Python: How to prevent subprocesses from receiving CTRL-C / Control-C / SIGINT

Question:

I am currently working on a wrapper for a dedicated server running in the shell. The wrapper spawns the server process via subprocess and observes and reacts to its output.

The dedicated server must be explicitly given a command to shut down gracefully. Thus, CTRL-C must not reach the server process.

If I capture the KeyboardInterrupt exception or overwrite the SIGINT-handler in python, the server process still receives the CTRL-C and stops immediately.

So my question is:
How to prevent subprocesses from receiving CTRL-C / Control-C / SIGINT?

Asked By: robert

||

Answers:

Try setting SIGINT to be ignored before spawning the subprocess (reset it to default behavior afterward).

If that doesn’t work, you’ll need to read up on job control and learn how to put a process in its own background process group, so that ^C doesn’t even cause the kernel to send the signal to it in the first place. (May not be possible in Python without writing C helpers.)

See also this older question.

Answered By: zwol

Somebody in the #python IRC-Channel (Freenode) helped me by pointing out the preexec_fn parameter of subprocess.Popen(…):

If preexec_fn is set to a callable
object, this object will be called in
the child process just before the
child is executed. (Unix only)

Thus, the following code solves the problem (UNIX only):

import subprocess
import signal

def preexec_function():
    # Ignore the SIGINT signal by setting the handler to the standard
    # signal handler SIG_IGN.
    signal.signal(signal.SIGINT, signal.SIG_IGN)

my_process = subprocess.Popen(
    ["my_executable"],
    preexec_fn = preexec_function
)

Note: The signal is actually not prevented from reaching the subprocess. Instead, the preexec_fn above overwrites the signal’s default handler so that the signal is ignored. Thus, this solution may not work if the subprocess overwrites the SIGINT handler again.

Another note: This solution works for all sorts of subprocesses, i.e. it is not restricted to subprocesses written in Python, too. For example the dedicated server I am writing my wrapper for is in fact written in Java.

Answered By: robert

Combining some of other answers that will do the trick – no signal sent to main app will be forwarded to the subprocess.

import os
from subprocess import Popen

def preexec(): # Don't forward signals.
    os.setpgrp()

Popen('whatever', preexec_fn = preexec)
Answered By: Marek Sapota

you can do something like this to make it work in windows and unix:

import subprocess
import sys

def pre_exec():
    # To ignore CTRL+C signal in the new process
    signal.signal(signal.SIGINT, signal.SIG_IGN)

if sys.platform.startswith('win'):
    #https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
    #CREATE_NEW_PROCESS_GROUP=0x00000200 -> If this flag is specified, CTRL+C signals will be disabled
    my_sub_process=subprocess.Popen(["executable"], creationflags=0x00000200)
else:
    my_sub_process=subprocess.Popen(["executable"], preexec_fn = pre_exec)
Answered By: jpastell

After an hour of various attempts, this works for me:

process = subprocess.Popen(["someprocess"], creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP)

It’s solution for windows.

Answered By: Danil Shaykhutdinov