mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-11-10 13:04:03 +03:00
Clear signal handlers when running processes
This commit is contained in:
parent
18dd13c872
commit
47d482dca9
@ -22,8 +22,9 @@ from .cli_stub import CLIOptions
|
||||
from .conf.utils import BadLine, KeyAction, to_cmdline
|
||||
from .config import common_opts_as_dict, prepare_config_file_for_editing
|
||||
from .constants import (
|
||||
appname, cache_dir, config_dir, is_macos, is_wayland, kitty_exe,
|
||||
logo_png_file, supports_primary_selection, website_url
|
||||
appname, cache_dir, clear_handled_signals, config_dir, handled_signals,
|
||||
is_macos, is_wayland, kitty_exe, logo_png_file, supports_primary_selection,
|
||||
website_url
|
||||
)
|
||||
from .fast_data_types import (
|
||||
CLOSE_BEING_CONFIRMED, GLFW_MOD_ALT, GLFW_MOD_CONTROL, GLFW_MOD_SHIFT,
|
||||
@ -733,6 +734,8 @@ class Boss:
|
||||
if not getattr(self, 'io_thread_started', False):
|
||||
self.child_monitor.start()
|
||||
self.io_thread_started = True
|
||||
for signum in self.child_monitor.handled_signals():
|
||||
handled_signals.add(signum)
|
||||
urls: List[str] = getattr(sys, 'cmdline_args_for_open', [])
|
||||
if urls:
|
||||
delattr(sys, 'cmdline_args_for_open')
|
||||
@ -1737,7 +1740,7 @@ class Boss:
|
||||
if stdin:
|
||||
r, w = safe_pipe(False)
|
||||
try:
|
||||
subprocess.Popen(cmd, env=env, stdin=r, cwd=cwd)
|
||||
subprocess.Popen(cmd, env=env, stdin=r, cwd=cwd, preexec_fn=clear_handled_signals)
|
||||
except Exception:
|
||||
os.close(w)
|
||||
else:
|
||||
@ -1745,7 +1748,7 @@ class Boss:
|
||||
finally:
|
||||
os.close(r)
|
||||
else:
|
||||
subprocess.Popen(cmd, env=env, cwd=cwd)
|
||||
subprocess.Popen(cmd, env=env, cwd=cwd, preexec_fn=clear_handled_signals)
|
||||
|
||||
def pipe(self, source: str, dest: str, exe: str, *args: str) -> Optional[Window]:
|
||||
cmd = [exe] + list(args)
|
||||
@ -1965,8 +1968,8 @@ class Boss:
|
||||
map f5 load_config_file /path/to/some/kitty.conf
|
||||
''')
|
||||
def load_config_file(self, *paths: str, apply_overrides: bool = True) -> None:
|
||||
from .config import load_config
|
||||
from .cli import default_config_paths
|
||||
from .config import load_config
|
||||
old_opts = get_options()
|
||||
prev_paths = old_opts.config_paths or default_config_paths(self.args.config)
|
||||
paths = paths or prev_paths
|
||||
|
@ -177,6 +177,17 @@ dealloc(ChildMonitor* self) {
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
handled_signals(ChildMonitor *self, PyObject *args UNUSED) {
|
||||
PyObject *ans = PyTuple_New(self->io_loop_data.num_handled_signals);
|
||||
if (ans) {
|
||||
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(ans); i++) {
|
||||
PyTuple_SET_ITEM(ans, i, PyLong_FromLong((long)self->io_loop_data.handled_signals[i]));
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
static void
|
||||
wakeup_io_loop(ChildMonitor *self, bool in_signal_handler) {
|
||||
wakeup_loop(&self->io_loop_data, in_signal_handler, "io_loop");
|
||||
@ -1706,6 +1717,7 @@ static PyMethodDef methods[] = {
|
||||
METHOD(main_loop, METH_NOARGS)
|
||||
METHOD(mark_for_close, METH_VARARGS)
|
||||
METHOD(resize_pty, METH_VARARGS)
|
||||
METHODB(handled_signals, METH_NOARGS),
|
||||
{"set_iutf8_winid", (PyCFunction)pyset_iutf8, METH_VARARGS, ""},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
@ -79,14 +79,16 @@ wait_for_terminal_ready(int fd) {
|
||||
|
||||
static PyObject*
|
||||
spawn(PyObject *self UNUSED, PyObject *args) {
|
||||
PyObject *argv_p, *env_p;
|
||||
PyObject *argv_p, *env_p, *handled_signals_p;
|
||||
int master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd;
|
||||
char *cwd, *exe;
|
||||
if (!PyArg_ParseTuple(args, "ssO!O!iiiiii", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd, &ready_read_fd, &ready_write_fd)) return NULL;
|
||||
if (!PyArg_ParseTuple(args, "ssO!O!iiiiiiO!", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd, &ready_read_fd, &ready_write_fd, &PyTuple_Type, &handled_signals_p)) return NULL;
|
||||
char name[2048] = {0};
|
||||
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||
char **argv = serialize_string_tuple(argv_p);
|
||||
char **env = serialize_string_tuple(env_p);
|
||||
int handled_signals[16] = {0}, num_handled_signals = MIN((int)arraysz(handled_signals), PyTuple_GET_SIZE(handled_signals_p));
|
||||
for (Py_ssize_t i = 0; i < num_handled_signals; i++) handled_signals[i] = PyLong_AsLong(PyTuple_GET_ITEM(handled_signals_p, i));
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03070000
|
||||
PyOS_BeforeFork();
|
||||
@ -99,10 +101,10 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
||||
PyOS_AfterFork_Child();
|
||||
#endif
|
||||
// See _Py_RestoreSignals in signalmodule.c for a list of signals python nukes
|
||||
sigset_t signals = {0};
|
||||
struct sigaction act = {.sa_handler=SIG_DFL};
|
||||
#define SA(which) { if (sigaction(which, &act, NULL) != 0) exit_on_err("sigaction() in child process failed"); }
|
||||
SA(SIGINT); SA(SIGTERM); SA(SIGCHLD); SA(SIGPIPE); SA(SIGUSR1); SA(SIGUSR2);
|
||||
const struct sigaction act = {.sa_handler=SIG_DFL};
|
||||
|
||||
#define SA(which) if (sigaction(which, &act, NULL) != 0) exit_on_err("sigaction() in child process failed");
|
||||
for (int si = 0; si < num_handled_signals; si++) { SA(handled_signals[si]); }
|
||||
#ifdef SIGXFSZ
|
||||
SA(SIGXFSZ);
|
||||
#endif
|
||||
@ -110,6 +112,7 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
||||
SA(SIGXFZ);
|
||||
#endif
|
||||
#undef SA
|
||||
sigset_t signals; sigemptyset(&signals);
|
||||
if (sigprocmask(SIG_SETMASK, &signals, NULL) != 0) exit_on_err("sigprocmask() in child process failed");
|
||||
// Use only signal-safe functions (man 7 signal-safety)
|
||||
if (chdir(cwd) != 0) { if (chdir("/") != 0) {} }; // ignore failure to chdir to /
|
||||
|
@ -13,7 +13,8 @@ from typing import (
|
||||
import kitty.fast_data_types as fast_data_types
|
||||
|
||||
from .constants import (
|
||||
is_freebsd, is_macos, kitty_base_dir, shell_path, terminfo_dir
|
||||
handled_signals, is_freebsd, is_macos, kitty_base_dir, shell_path,
|
||||
terminfo_dir
|
||||
)
|
||||
from .types import run_once
|
||||
from .utils import log_error, which
|
||||
@ -289,7 +290,9 @@ class Child:
|
||||
argv[0] = (f'-{exe.split("/")[-1]}')
|
||||
self.final_exe = which(exe) or exe
|
||||
self.final_argv0 = argv[0]
|
||||
pid = fast_data_types.spawn(self.final_exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd)
|
||||
pid = fast_data_types.spawn(
|
||||
self.final_exe, self.cwd, tuple(argv), env, master, slave,
|
||||
stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd, tuple(handled_signals))
|
||||
os.close(slave)
|
||||
self.pid = pid
|
||||
self.child_fd = master
|
||||
|
@ -11,7 +11,9 @@ from typing import (
|
||||
|
||||
from .cli_stub import CLIOptions
|
||||
from .conf.utils import resolve_config
|
||||
from .constants import appname, defconf, is_macos, str_version, website_url
|
||||
from .constants import (
|
||||
appname, clear_handled_signals, defconf, is_macos, str_version, website_url
|
||||
)
|
||||
from .options.types import Options as KittyOpts
|
||||
from .typing import BadLineType, TypedDict
|
||||
|
||||
@ -362,7 +364,7 @@ class PrintHelpForSeq:
|
||||
text = '\n'.join(blocks) + '\n\n' + version()
|
||||
if print_help_for_seq.allow_pager and sys.stdout.isatty():
|
||||
import subprocess
|
||||
p = subprocess.Popen(['less', '-isRXF'], stdin=subprocess.PIPE)
|
||||
p = subprocess.Popen(['less', '-isRXF'], stdin=subprocess.PIPE, preexec_fn=clear_handled_signals)
|
||||
try:
|
||||
p.communicate(text.encode('utf-8'))
|
||||
except KeyboardInterrupt:
|
||||
|
@ -6,7 +6,7 @@ import os
|
||||
import pwd
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
from typing import TYPE_CHECKING, Iterable, NamedTuple, Optional, Set
|
||||
from typing import TYPE_CHECKING, Any, Iterable, NamedTuple, Optional, Set
|
||||
|
||||
from .types import run_once
|
||||
|
||||
@ -235,3 +235,16 @@ def website_url(doc_name: str = '') -> str:
|
||||
if doc_name:
|
||||
doc_name += '/'
|
||||
return f'https://sw.kovidgoyal.net/kitty/{doc_name}'
|
||||
|
||||
|
||||
handled_signals: Set[int] = set()
|
||||
|
||||
|
||||
def clear_handled_signals(*a: Any) -> None:
|
||||
if not handled_signals:
|
||||
return
|
||||
import signal
|
||||
if hasattr(signal, 'pthread_sigmask'):
|
||||
signal.pthread_sigmask(signal.SIG_UNBLOCK, handled_signals)
|
||||
for s in handled_signals:
|
||||
signal.signal(s, signal.SIG_DFL)
|
||||
|
@ -1200,6 +1200,9 @@ class ChildMonitor:
|
||||
def wakeup(self) -> None:
|
||||
pass
|
||||
|
||||
def handled_signals(self) -> Tuple[int, ...]:
|
||||
pass
|
||||
|
||||
def main_loop(self) -> None:
|
||||
pass
|
||||
|
||||
@ -1279,7 +1282,8 @@ def spawn(
|
||||
stdin_read_fd: int,
|
||||
stdin_write_fd: int,
|
||||
ready_read_fd: int,
|
||||
ready_write_fd: int
|
||||
ready_write_fd: int,
|
||||
handled_signals: Tuple[int, ...],
|
||||
) -> int:
|
||||
pass
|
||||
|
||||
|
@ -16,8 +16,9 @@ from .cli_stub import CLIOptions
|
||||
from .conf.utils import BadLine
|
||||
from .config import cached_values_for
|
||||
from .constants import (
|
||||
appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
|
||||
is_wayland, kitty_exe, logo_png_file, running_in_kitty, website_url
|
||||
appname, beam_cursor_data_file, clear_handled_signals, config_dir,
|
||||
glfw_path, is_macos, is_wayland, kitty_exe, logo_png_file,
|
||||
running_in_kitty, website_url
|
||||
)
|
||||
from .fast_data_types import (
|
||||
GLFW_IBEAM_CURSOR, GLFW_MOD_ALT, GLFW_MOD_SHIFT, create_os_window,
|
||||
@ -249,7 +250,7 @@ def setup_profiling() -> Generator[None, None, None]:
|
||||
with open(cg, 'wb') as f:
|
||||
subprocess.call(['pprof', '--callgrind', exe, '/tmp/kitty-profile.log'], stdout=f)
|
||||
try:
|
||||
subprocess.Popen(['kcachegrind', cg])
|
||||
subprocess.Popen(['kcachegrind', cg], preexec_fn=clear_handled_signals)
|
||||
except FileNotFoundError:
|
||||
subprocess.call(['pprof', '--text', exe, '/tmp/kitty-profile.log'])
|
||||
print('To view the graphical call data, use: kcachegrind', cg)
|
||||
|
@ -9,7 +9,9 @@ from typing import Dict, NamedTuple, Optional, Tuple
|
||||
from urllib.request import urlopen
|
||||
|
||||
from .config import atomic_save
|
||||
from .constants import Version, cache_dir, kitty_exe, version, website_url
|
||||
from .constants import (
|
||||
Version, cache_dir, clear_handled_signals, kitty_exe, version, website_url
|
||||
)
|
||||
from .fast_data_types import add_timer, get_boss, monitor_pid
|
||||
from .notify import notify
|
||||
from .utils import log_error, open_url
|
||||
@ -115,7 +117,7 @@ def update_check() -> bool:
|
||||
p = subprocess.Popen([
|
||||
kitty_exe(), '+runpy',
|
||||
'from kitty.update_check import run_worker; run_worker()'
|
||||
], stdout=subprocess.PIPE)
|
||||
], stdout=subprocess.PIPE, preexec_fn=clear_handled_signals)
|
||||
except OSError as e:
|
||||
log_error(f'Failed to run kitty for update check, with error: {e}')
|
||||
return False
|
||||
|
@ -18,8 +18,8 @@ from typing import (
|
||||
)
|
||||
|
||||
from .constants import (
|
||||
appname, config_dir, is_macos, is_wayland, read_kitty_resource,
|
||||
runtime_dir, shell_path, ssh_control_master_template,
|
||||
appname, clear_handled_signals, config_dir, is_macos, is_wayland,
|
||||
read_kitty_resource, runtime_dir, shell_path, ssh_control_master_template,
|
||||
supports_primary_selection
|
||||
)
|
||||
from .fast_data_types import Color, open_tty
|
||||
@ -277,7 +277,9 @@ def open_cmd(cmd: Union[Iterable[str], List[str]], arg: Union[None, Iterable[str
|
||||
cmd.append(arg)
|
||||
else:
|
||||
cmd.extend(arg)
|
||||
return subprocess.Popen(tuple(cmd), stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cwd or None)
|
||||
return subprocess.Popen(
|
||||
tuple(cmd), stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cwd or None,
|
||||
preexec_fn=clear_handled_signals)
|
||||
|
||||
|
||||
def open_url(url: str, program: Union[str, List[str]] = 'default', cwd: Optional[str] = None) -> 'PopenType[bytes]':
|
||||
@ -797,7 +799,9 @@ def read_shell_environment(opts: Optional[Options] = None) -> Dict[str, str]:
|
||||
if '-i' not in shell and '--interactive' not in shell:
|
||||
shell += ['-i']
|
||||
try:
|
||||
p = subprocess.Popen(shell + ['-c', 'env'], stdout=slave, stdin=slave, stderr=slave, start_new_session=True, close_fds=True)
|
||||
p = subprocess.Popen(
|
||||
shell + ['-c', 'env'], stdout=slave, stdin=slave, stderr=slave, start_new_session=True, close_fds=True,
|
||||
preexec_fn=clear_handled_signals)
|
||||
except FileNotFoundError:
|
||||
log_error('Could not find shell to read environment')
|
||||
return ans
|
||||
@ -970,8 +974,8 @@ def cleanup_ssh_control_masters() -> None:
|
||||
except OSError:
|
||||
return
|
||||
workers = tuple(subprocess.Popen([
|
||||
'ssh', '-o', f'ControlPath={x}', '-O', 'exit', 'kitty-unused-host-name'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
for x in files)
|
||||
'ssh', '-o', f'ControlPath={x}', '-O', 'exit', 'kitty-unused-host-name'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
||||
preexec_fn=clear_handled_signals) for x in files)
|
||||
for w in workers:
|
||||
w.wait()
|
||||
for x in files:
|
||||
|
@ -21,7 +21,9 @@ from typing import (
|
||||
from .child import ProcessDesc
|
||||
from .cli_stub import CLIOptions
|
||||
from .config import build_ansi_color_table
|
||||
from .constants import appname, config_dir, is_macos, wakeup
|
||||
from .constants import (
|
||||
appname, clear_handled_signals, config_dir, is_macos, wakeup
|
||||
)
|
||||
from .fast_data_types import (
|
||||
BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM,
|
||||
CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK,
|
||||
@ -831,9 +833,10 @@ class Window:
|
||||
set_clipboard_string(url)
|
||||
|
||||
def handle_remote_file(self, netloc: str, remote_path: str) -> None:
|
||||
from .utils import SSHConnectionData
|
||||
from kittens.ssh.main import get_connection_data
|
||||
from kittens.remote_file.main import is_ssh_kitten_sentinel
|
||||
from kittens.ssh.main import get_connection_data
|
||||
|
||||
from .utils import SSHConnectionData
|
||||
args = self.ssh_kitten_cmdline()
|
||||
conn_data: Union[None, List[str], SSHConnectionData] = None
|
||||
if args:
|
||||
@ -903,7 +906,7 @@ class Window:
|
||||
import subprocess
|
||||
env = self.child.foreground_environ
|
||||
env['KITTY_CHILD_CMDLINE'] = ' '.join(map(shlex.quote, self.child.cmdline))
|
||||
subprocess.Popen(cb, env=env, cwd=self.child.foreground_cwd)
|
||||
subprocess.Popen(cb, env=env, cwd=self.child.foreground_cwd, preexec_fn=clear_handled_signals)
|
||||
if not self.is_active:
|
||||
changed = not self.needs_attention
|
||||
self.needs_attention = True
|
||||
|
Loading…
Reference in New Issue
Block a user