Run the child process

This commit is contained in:
Kovid Goyal
2016-10-16 17:59:19 +05:30
parent 1065a88cef
commit 29ba13835a
3 changed files with 60 additions and 10 deletions

View File

@@ -2,15 +2,11 @@
# vim:fileencoding=utf-8 # vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import os
import fcntl
import termios
import struct
from PyQt5.QtCore import QObject from PyQt5.QtCore import QObject
from .screen import Screen from .screen import Screen
from .term import TerminalWidget from .term import TerminalWidget
from .utils import resize_pty, hangup
class Boss(QObject): class Boss(QObject):
@@ -20,7 +16,7 @@ class Boss(QObject):
self.screen = Screen(opts, parent=self) self.screen = Screen(opts, parent=self)
self.term = TerminalWidget(opts, self.screen.linebuf, parent) self.term = TerminalWidget(opts, self.screen.linebuf, parent)
self.term.relayout_lines.connect(self.relayout_lines) self.term.relayout_lines.connect(self.relayout_lines)
self.master_fd, self.slave_fd = os.openpty() resize_pty(self.screen.columns, self.screen.lines)
def apply_opts(self, opts): def apply_opts(self, opts):
self.screen.apply_opts(opts) self.screen.apply_opts(opts)
@@ -28,9 +24,9 @@ class Boss(QObject):
def relayout_lines(self, previous, cells_per_line, previousl, lines_per_screen): def relayout_lines(self, previous, cells_per_line, previousl, lines_per_screen):
self.screen.resize(lines_per_screen, cells_per_line) self.screen.resize(lines_per_screen, cells_per_line)
fcntl.ioctl(self.master_fd, termios.TIOCSWINSZ, struct.pack('4H', cells_per_line, lines_per_screen, 0, 0)) resize_pty(cells_per_line, lines_per_screen)
def shutdown(self): def shutdown(self):
os.close(self.slave_fd), os.close(self.master_fd)
del self.master_fd del self.master_fd
del self.slave_fd del self.slave_fd
hangup()

View File

@@ -16,6 +16,7 @@ from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
from .config import load_config, validate_font from .config import load_config, validate_font
from .constants import appname, str_version, config_dir from .constants import appname, str_version, config_dir
from .boss import Boss from .boss import Boss
from .utils import fork_child
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
@@ -70,7 +71,7 @@ def option_parser():
a('--class', default=appname, dest='cls', help=_('Set the class part of the WM_CLASS property')) a('--class', default=appname, dest='cls', help=_('Set the class part of the WM_CLASS property'))
a('--config', default=os.path.join(config_dir, 'kitty.conf'), help=_('Specify a path to the config file to use')) a('--config', default=os.path.join(config_dir, 'kitty.conf'), help=_('Specify a path to the config file to use'))
a('--cmd', '-c', default=None, help=_('Run python code in the kitty context')) a('--cmd', '-c', default=None, help=_('Run python code in the kitty context'))
a('--exec', '-e', default=pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh', help=_('Run the specified command instead of the shell')) a('--exec', '-e', dest='child', default=pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh', help=_('Run the specified command instead of the shell'))
a('-d', '--directory', default='.', help=_('Change to the specified directory when launching')) a('-d', '--directory', default='.', help=_('Change to the specified directory when launching'))
a('--version', action='version', version='{} {} by Kovid Goyal'.format(appname, '.'.join(str_version))) a('--version', action='version', version='{} {} by Kovid Goyal'.format(appname, '.'.join(str_version)))
return parser return parser
@@ -81,8 +82,10 @@ def main():
if args.cmd: if args.cmd:
exec(args.cmd) exec(args.cmd)
return return
# Ensure the child process gets no environment from Qt
opts = load_config(args.config) opts = load_config(args.config)
os.chdir(args.directory) fork_child(args.child, args.directory, opts)
QApplication.setAttribute(Qt.AA_DisableHighDpiScaling, True) QApplication.setAttribute(Qt.AA_DisableHighDpiScaling, True)
app = QApplication([appname]) app = QApplication([appname])
app.setOrganizationName(args.cls) app.setOrganizationName(args.cls)

View File

@@ -2,6 +2,12 @@
# vim:fileencoding=utf-8 # vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import os
import termios
import struct
import shlex
import fcntl
import signal
from functools import lru_cache from functools import lru_cache
from PyQt5.QtGui import QFontMetrics from PyQt5.QtGui import QFontMetrics
@@ -24,3 +30,48 @@ def set_current_font_metrics(fm: QFontMetrics, cw: int) -> None:
global current_font_metrics, cell_width global current_font_metrics, cell_width
current_font_metrics, cell_width = fm, cw current_font_metrics, cell_width = fm, cw
wcwidth.cache_clear() wcwidth.cache_clear()
def create_pty():
if not hasattr(create_pty, 'master'):
create_pty.master, create_pty.slave = os.openpty()
fcntl.fcntl(create_pty.slave, fcntl.F_SETFD, fcntl.fcntl(create_pty.slave, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC)
return create_pty.master, create_pty.slave
def fork_child(cmd, cwd, opts):
argv = shlex.split(cmd)
master, slave = create_pty()
pid = os.fork()
if pid == 0:
try:
os.chdir(cwd)
except EnvironmentError:
os.chdir('/')
os.setsid()
for i in range(3):
os.dup2(slave, i)
os.close(slave), os.close(master)
# Establish the controlling terminal (see man 7 credentials)
os.close(os.open(os.ttyname(1), os.O_RDWR))
os.environ['TERM'] = opts.term
os.environ['COLORTERM'] = 'truecolor'
os.execvp(argv[0], argv)
else:
os.close(slave)
fork_child.pid = pid
return pid
def resize_pty(w, h):
master = create_pty()[0]
fcntl.ioctl(master, termios.TIOCSWINSZ, struct.pack('4H', w, h, 0, 0))
def hangup():
if hasattr(fork_child, 'pid'):
pid = fork_child.pid
del fork_child.pid
pgrp = os.getpgid(pid)
os.killpg(pgrp, signal.SIGHUP)
os.close(create_pty()[0])