From bdade7e15153680d1748bc5ee1c7baaf079add38 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 31 Jul 2019 11:34:54 +0530 Subject: [PATCH] Use a new controlling terminal when reading shell environment This is because some people do things in their rc files based on checking the name of the controlling terminal. --- kitty/child.py | 17 +++++++++++++---- kitty/main.py | 42 ++++++++++++++++++++++++++++-------------- kitty/session.py | 4 ++-- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/kitty/child.py b/kitty/child.py index cc9b48afd..63037e02b 100644 --- a/kitty/child.py +++ b/kitty/child.py @@ -118,6 +118,10 @@ def remove_cloexec(fd): fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.fcntl(fd, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC) +def remove_blocking(fd): + os.set_blocking(fd, False) + + def default_env(): try: return default_env.env @@ -132,6 +136,13 @@ def set_default_env(val=None): default_env.env = env +def openpty(): + master, slave = os.openpty() # Note that master and slave are in blocking mode + remove_cloexec(slave) + fast_data_types.set_iutf8_fd(master, True) + return master, slave + + class Child: child_fd = pid = None @@ -178,9 +189,7 @@ class Child: if self.forked: return self.forked = True - master, slave = os.openpty() # Note that master and slave are in blocking mode - remove_cloexec(slave) - fast_data_types.set_iutf8_fd(master, True) + master, slave = openpty() stdin, self.stdin = self.stdin, None ready_read_fd, ready_write_fd = os.pipe() remove_cloexec(ready_read_fd) @@ -206,7 +215,7 @@ class Child: fast_data_types.thread_write(stdin_write_fd, stdin) os.close(ready_read_fd) self.terminal_ready_fd = ready_write_fd - fcntl.fcntl(self.child_fd, fcntl.F_SETFL, fcntl.fcntl(self.child_fd, fcntl.F_GETFL) | os.O_NONBLOCK) + remove_blocking(self.child_fd) return pid def mark_terminal_ready(self): diff --git a/kitty/main.py b/kitty/main.py index eaa35f38c..5c42dae1a 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -10,7 +10,7 @@ from contextlib import contextmanager, suppress from .borders import load_borders_program from .boss import Boss -from .child import set_default_env +from .child import set_default_env, openpty, remove_blocking from .cli import create_opts, parse_args from .config import cached_values_for, initial_window_size_func from .constants import ( @@ -203,23 +203,37 @@ def macos_cmdline(argv_args): return ans -def read_shell_environment(opts): +def read_shell_environment(opts=None): if not hasattr(read_shell_environment, 'ans'): import subprocess from .session import resolved_shell shell = resolved_shell(opts) - p = subprocess.Popen(shell + ['-l', '-c', 'env'], stdout=subprocess.PIPE) - raw = p.stdout.read() - if p.wait() == 0: - raw = raw.decode('utf-8', 'replace') - ans = read_shell_environment.ans = {} - for line in raw.splitlines(): - k, v = line.partition('=')[::2] - if k and v: - ans[k] = v - else: - log_error('Failed to run shell to read its environment') - read_shell_environment.ans = {} + master, slave = openpty() + remove_blocking(master) + p = subprocess.Popen(shell + ['-l', '-c', 'env'], stdout=slave, stdin=slave, stderr=slave, start_new_session=True, close_fds=True) + with os.fdopen(master, 'rb') as stdout, os.fdopen(slave, 'wb'): + raw = b'' + while p.wait(0.01) is None: + with suppress(Exception): + raw += stdout.read() + if p.returncode == 0: + while True: + try: + x = stdout.read() + except Exception: + break + if not x: + break + raw += x + raw = raw.decode('utf-8', 'replace') + ans = read_shell_environment.ans = {} + for line in raw.splitlines(): + k, v = line.partition('=')[::2] + if k and v: + ans[k] = v + else: + log_error('Failed to run shell to read its environment') + read_shell_environment.ans = {} return read_shell_environment.ans diff --git a/kitty/session.py b/kitty/session.py index ae295b486..57038286f 100644 --- a/kitty/session.py +++ b/kitty/session.py @@ -74,8 +74,8 @@ class Session: self.tabs[-1].cwd = val -def resolved_shell(opts): - ans = opts.shell +def resolved_shell(opts=None): + ans = getattr(opts, 'shell', '.') if ans == '.': ans = [shell_path] else: