Skip to content

Commit ced282c

Browse files
committed
Infrastructure to pass arbitrary fds to spawned child
1 parent ec4f498 commit ced282c

File tree

3 files changed

+29
-4
lines changed

3 files changed

+29
-4
lines changed

kitty/child.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ wait_for_terminal_ready(int fd) {
7979

8080
static PyObject*
8181
spawn(PyObject *self UNUSED, PyObject *args) {
82-
PyObject *argv_p, *env_p, *handled_signals_p;
82+
PyObject *argv_p, *env_p, *handled_signals_p, *pass_fds;
8383
int master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd, forward_stdio;
8484
const char *kitten_exe;
8585
char *cwd, *exe;
86-
if (!PyArg_ParseTuple(args, "ssO!O!iiiiiiO!sp", &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, &kitten_exe, &forward_stdio)) return NULL;
86+
if (!PyArg_ParseTuple(args, "ssO!O!iiiiiiO!spO!", &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, &kitten_exe, &forward_stdio, &PyTuple_Type, &pass_fds)) return NULL;
8787
char name[2048] = {0};
8888
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
8989
char **argv = serialize_string_tuple(argv_p);
@@ -130,6 +130,15 @@ spawn(PyObject *self UNUSED, PyObject *args) {
130130
safe_close(tfd, __FILE__, __LINE__);
131131

132132
int min_closed_fd = 3;
133+
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(pass_fds); i++) {
134+
PyObject *pfd = PyTuple_GET_ITEM(pass_fds, i);
135+
if (!PyLong_Check(pfd)) exit_on_err("pass_fds must contain only integers");
136+
int fd = PyLong_AsLong(pfd);
137+
if (fd > -1) {
138+
if (fd == min_closed_fd) min_closed_fd++;
139+
else if (safe_dup2(fd, min_closed_fd++) == -1) exit_on_err("dup2() failed for forwarded fd 1");
140+
}
141+
}
133142
if (forward_stdio) {
134143
if (safe_dup2(STDOUT_FILENO, min_closed_fd++) == -1) exit_on_err("dup2() failed for forwarded fd 1");
135144
if (safe_dup2(STDERR_FILENO, min_closed_fd++) == -1) exit_on_err("dup2() failed for forwarded fd 2");

kitty/child.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from collections.abc import Generator, Sequence
88
from contextlib import contextmanager, suppress
99
from itertools import count
10-
from typing import TYPE_CHECKING, DefaultDict, Optional
10+
from typing import TYPE_CHECKING, DefaultDict, Optional, Protocol, Union
1111

1212
import kitty.fast_data_types as fast_data_types
1313

@@ -23,6 +23,12 @@
2323
from .window import CwdRequest
2424

2525

26+
class InheritableFile(Protocol):
27+
28+
def close(self) -> None: ...
29+
def fileno(self) -> int: ...
30+
31+
2632
if is_macos:
2733
from kitty.fast_data_types import cmdline_of_process as cmdline_
2834
from kitty.fast_data_types import cwd_of_process as _cwd
@@ -211,11 +217,13 @@ def __init__(
211217
is_clone_launch: str = '',
212218
add_listen_on_env_var: bool = True,
213219
hold: bool = False,
220+
pass_fds: tuple[Union[int, InheritableFile], ...] = (),
214221
):
215222
self.is_clone_launch = is_clone_launch
216223
self.id = next(child_counter)
217224
self.add_listen_on_env_var = add_listen_on_env_var
218225
self.argv = list(argv)
226+
self.pass_fds = pass_fds
219227
if cwd_from:
220228
try:
221229
cwd = cwd_from.modify_argv_for_launch_with_cwd(self.argv, env) or cwd
@@ -331,10 +339,17 @@ def fork(self) -> Optional[int]:
331339
argv = cmdline_for_hold(argv)
332340
final_exe = argv[0]
333341
env = tuple(f'{k}={v}' for k, v in self.final_env.items())
342+
pass_fds = tuple(sorted(x if isinstance(x, int) else x.fileno() for x in self.pass_fds))
334343
pid = fast_data_types.spawn(
335344
final_exe, cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd,
336-
ready_read_fd, ready_write_fd, tuple(handled_signals), kitten_exe(), opts.forward_stdio)
345+
ready_read_fd, ready_write_fd, tuple(handled_signals), kitten_exe(), opts.forward_stdio, pass_fds)
337346
os.close(slave)
347+
for x in self.pass_fds:
348+
if isinstance(x, int):
349+
os.close(x)
350+
else:
351+
x.close()
352+
self.pass_fds = ()
338353
self.pid = pid
339354
self.child_fd = master
340355
if stdin is not None:

kitty/fast_data_types.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,7 @@ def spawn(
14601460
handled_signals: Tuple[int, ...],
14611461
kitten_exe: str,
14621462
forward_stdio: bool,
1463+
pass_fds: tuple[int, ...],
14631464
) -> int:
14641465
pass
14651466

0 commit comments

Comments
 (0)