diff --git a/kitty/boss.py b/kitty/boss.py index bba6ad079..1ed46ef7c 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -2,7 +2,9 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal +import atexit import re +import socket from functools import partial from gettext import gettext as _ from weakref import WeakValueDictionary @@ -24,7 +26,8 @@ from .session import create_session from .tabs import SpecialWindow, SpecialWindowInstance, TabManager from .utils import ( end_startup_notification, get_primary_selection, init_startup_notification, - open_url, safe_print, set_primary_selection, single_instance + open_url, remove_socket_file, safe_print, set_primary_selection, + single_instance ) @@ -33,6 +36,29 @@ def initialize_renderer(): prerender() +def listen_on(spec): + protocol, rest = spec.split(':', 1) + socket_path = None + if protocol == 'unix': + family = socket.AF_UNIX + address = rest + if address.startswith('@') and len(address) > 1: + address = '\0' + address[1:] + else: + socket_path = address + elif protocol in ('tcp', 'tcp6'): + family = socket.AF_INET if protocol == 'tcp' else socket.AF_INET6 + host, port = rest.rsplit(':', 1) + address = host, int(port) + else: + raise ValueError('Unknown protocol in --listen-on value: {}'.format(spec)) + s = socket.socket(family) + atexit.register(remove_socket_file, s, socket_path) + s.bind(address) + s.listen() + return s.fileno() + + class DumpCommands: # {{{ def __init__(self, args): @@ -70,10 +96,13 @@ class Boss: self.shutting_down = False talk_fd = getattr(single_instance, 'socket', None) talk_fd = -1 if talk_fd is None else talk_fd.fileno() + listen_fd = -1 + if opts.allow_remote_control and args.listen_on: + listen_fd = listen_on(args.listen_on) self.child_monitor = ChildMonitor( self.on_child_death, DumpCommands(args) if args.dump_commands or args.dump_bytes else None, - talk_fd + talk_fd, listen_fd ) set_boss(self) self.current_font_size = opts.font_size diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 56d06a767..fd6bfffd4 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -36,7 +36,7 @@ typedef struct { bool shutting_down; pthread_t io_thread, talk_thread; - int talk_fd; + int talk_fd, listen_fd; Message *messages; size_t messages_capacity, messages_count; } ChildMonitor; @@ -120,11 +120,11 @@ static PyObject * new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { ChildMonitor *self; PyObject *dump_callback, *death_notify; - int talk_fd = -1; + int talk_fd = -1, listen_fd = -1; int ret; if (the_monitor) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; } - if (!PyArg_ParseTuple(args, "OO|i", &death_notify, &dump_callback, &talk_fd)) return NULL; + if (!PyArg_ParseTuple(args, "OO|ii", &death_notify, &dump_callback, &talk_fd, &listen_fd)) return NULL; if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) { PyErr_Format(PyExc_RuntimeError, "Failed to create children_lock mutex: %s", strerror(ret)); return NULL; @@ -138,6 +138,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { if (siginterrupt(SIGTERM, false) != 0) return PyErr_SetFromErrno(PyExc_OSError); self = (ChildMonitor *)type->tp_alloc(type, 0); self->talk_fd = talk_fd; + self->listen_fd = listen_fd; if (self == NULL) return PyErr_NoMemory(); self->death_notify = death_notify; Py_INCREF(death_notify); if (dump_callback != Py_None) { @@ -191,7 +192,7 @@ static void send_response(int fd, const char *msg, size_t msg_sz); static PyObject * start(ChildMonitor *self) { #define start_doc "start() -> Start the I/O thread" - if (self->talk_fd > -1) { + if (self->talk_fd > -1 || self->listen_fd > -1) { if (pthread_create(&self->talk_thread, NULL, talk_loop, self) != 0) return PyErr_SetFromErrno(PyExc_OSError); } int ret = pthread_create(&self->io_thread, NULL, io_loop, self); diff --git a/kitty/cli.py b/kitty/cli.py index 48a0e831e..e2eef34c8 100644 --- a/kitty/cli.py +++ b/kitty/cli.py @@ -84,6 +84,15 @@ with the same |_ --instance-group| will result in new windows being created in the first |_ {appname}| instance within that group +--listen-on +Tell kitty to listen on the specified UNIX socket or TCP port for control +messages. For example, --listen-on=unix:/tmp/mykitty or +--listen-on=tcp:localhost:12345. On Linux systems, you can also use abstract +UNIX sockets, not associated with a file, like this: --listen-on=unix:@mykitty. +Note that this option will be ignored, unless you set allow_remote_control to yes +in kitty.conf + + # Debugging options --version -v