From 85cb6c3c705424ff3352d4771efa4c4d0dd09590 Mon Sep 17 00:00:00 2001
From: Alyssa Ross <hi@alyssa.is>
Date: Fri, 10 Dec 2021 17:40:59 +0000
Subject: [PATCH] Add support for opening an existing PTY
---
main.c | 13 ++++++++++-
server.c | 2 +-
terminal.c | 68 ++++++++++++++++++++++++++++++++----------------------
terminal.h | 5 ++--
4 files changed, 56 insertions(+), 32 deletions(-)
diff --git a/main.c b/main.c
index dffd2b2b..aa060747 100644
--- a/main.c
+++ b/main.c
@@ -3,6 +3,7 @@
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
+#include <limits.h>
#include <locale.h>
#include <getopt.h>
#include <signal.h>
@@ -174,6 +175,10 @@ sanitize_signals(void)
sigaction(i, &dfl, NULL);
}
+enum {
+ PTY_OPTION = CHAR_MAX + 1,
+};
+
int
main(int argc, char *const *argv)
{
@@ -211,6 +216,7 @@ main(int argc, char *const *argv)
{"maximized", no_argument, NULL, 'm'},
{"fullscreen", no_argument, NULL, 'F'},
{"presentation-timings", no_argument, NULL, 'P'}, /* Undocumented */
+ {"pty", required_argument, NULL, PTY_OPTION},
{"print-pid", required_argument, NULL, 'p'},
{"log-level", required_argument, NULL, 'd'},
{"log-colorize", optional_argument, NULL, 'l'},
@@ -223,6 +229,7 @@ main(int argc, char *const *argv)
bool check_config = false;
const char *conf_path = NULL;
const char *custom_cwd = NULL;
+ const char *pty_path = NULL;
bool as_server = false;
const char *conf_server_socket_path = NULL;
bool presentation_timings = false;
@@ -318,6 +325,10 @@ main(int argc, char *const *argv)
conf_server_socket_path = optarg;
break;
+ case PTY_OPTION:
+ pty_path = optarg;
+ break;
+
case 'P':
presentation_timings = true;
break;
@@ -576,7 +587,7 @@ main(int argc, char *const *argv)
goto out;
if (!as_server && (term = term_init(
- &conf, fdm, reaper, wayl, "foot", cwd, token,
+ &conf, fdm, reaper, wayl, "foot", cwd, token, pty_path,
argc, argv, NULL,
&term_shutdown_cb, &shutdown_ctx)) == NULL) {
goto out;
diff --git a/server.c b/server.c
index ca55b8f3..b1268574 100644
--- a/server.c
+++ b/server.c
@@ -332,7 +332,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data)
instance->terminal = term_init(
conf != NULL ? conf : server->conf,
server->fdm, server->reaper, server->wayl, "footclient", cwd, token,
- cdata.argc, argv, envp, &term_shutdown_handler, instance);
+ NULL, cdata.argc, argv, envp, &term_shutdown_handler, instance);
if (instance->terminal == NULL) {
LOG_ERR("failed to instantiate new terminal");
diff --git a/terminal.c b/terminal.c
index d4132c24..ad10bb7d 100644
--- a/terminal.c
+++ b/terminal.c
@@ -367,6 +367,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
del_utmp_record(term->conf, term->reaper, term->ptmx);
fdm_del(fdm, fd);
term->ptmx = -1;
+ term_shutdown(term);
}
return true;
@@ -1056,11 +1057,14 @@ load_fonts_from_conf(struct terminal *term)
static void fdm_client_terminated(
struct reaper *reaper, pid_t pid, int status, void *data);
+static const int PTY_OPEN_FLAGS = O_RDWR | O_NOCTTY;
+
struct terminal *
term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
struct wayland *wayl, const char *foot_exe, const char *cwd,
- const char *token, int argc, char *const *argv, char *const *envp,
- void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data)
+ const char *token, const char *pty_path, int argc, char *const *argv,
+ char *const *envp, void (*shutdown_cb)(void *data, int exit_code),
+ void *shutdown_data)
{
int ptmx = -1;
int flash_fd = -1;
@@ -1075,7 +1079,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
return NULL;
}
- if ((ptmx = posix_openpt(O_RDWR | O_NOCTTY)) < 0) {
+ ptmx = pty_path ? open(pty_path, PTY_OPEN_FLAGS) : posix_openpt(PTY_OPEN_FLAGS);
+ if (ptmx < 0) {
LOG_ERRNO("failed to open PTY");
goto close_fds;
}
@@ -1142,6 +1147,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.fdm = fdm,
.reaper = reaper,
.conf = conf,
+ .slave = -1,
.ptmx = ptmx,
.ptmx_buffers = tll_init(),
.ptmx_paste_buffers = tll_init(),
@@ -1272,16 +1278,18 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
add_utmp_record(conf, reaper, ptmx);
- /* Start the slave/client */
- if ((term->slave = slave_spawn(
- term->ptmx, argc, term->cwd, argv, envp, &conf->env_vars,
- conf->term, conf->shell, conf->login_shell,
- &conf->notifications)) == -1)
- {
- goto err;
- }
+ if (!pty_path) {
+ /* Start the slave/client */
+ if ((term->slave = slave_spawn(
+ term->ptmx, argc, term->cwd, argv, envp, &conf->env_vars,
+ conf->term, conf->shell, conf->login_shell,
+ &conf->notifications)) == -1)
+ {
+ goto err;
+ }
- reaper_add(term->reaper, term->slave, &fdm_client_terminated, term);
+ reaper_add(term->reaper, term->slave, &fdm_client_terminated, term);
+ }
/* Guess scale; we're not mapped yet, so we don't know on which
* output we'll be. Use scaling factor from first monitor */
@@ -1541,26 +1549,30 @@ term_shutdown(struct terminal *term)
close(term->ptmx);
if (!term->shutdown.client_has_terminated) {
- LOG_DBG("initiating asynchronous terminate of slave; "
- "sending SIGTERM to PID=%u", term->slave);
+ if (term->slave <= 0) {
+ term->shutdown.client_has_terminated = true;
+ } else {
+ LOG_DBG("initiating asynchronous terminate of slave; "
+ "sending SIGTERM to PID=%u", term->slave);
- kill(-term->slave, SIGTERM);
+ kill(-term->slave, SIGTERM);
- const struct itimerspec timeout = {.it_value = {.tv_sec = 60}};
+ const struct itimerspec timeout = {.it_value = {.tv_sec = 60}};
- int timeout_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
- if (timeout_fd < 0 ||
- timerfd_settime(timeout_fd, 0, &timeout, NULL) < 0 ||
- !fdm_add(term->fdm, timeout_fd, EPOLLIN, &fdm_terminate_timeout, term))
- {
- if (timeout_fd >= 0)
- close(timeout_fd);
- LOG_ERRNO("failed to create slave terminate timeout FD");
- return false;
+ int timeout_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
+ if (timeout_fd < 0 ||
+ timerfd_settime(timeout_fd, 0, &timeout, NULL) < 0 ||
+ !fdm_add(term->fdm, timeout_fd, EPOLLIN, &fdm_terminate_timeout, term))
+ {
+ if (timeout_fd >= 0)
+ close(timeout_fd);
+ LOG_ERRNO("failed to create slave terminate timeout FD");
+ return false;
+ }
+
+ xassert(term->shutdown.terminate_timeout_fd < 0);
+ term->shutdown.terminate_timeout_fd = timeout_fd;
}
-
- xassert(term->shutdown.terminate_timeout_fd < 0);
- term->shutdown.terminate_timeout_fd = timeout_fd;
}
term->selection.auto_scroll.fd = -1;
diff --git a/terminal.h b/terminal.h
index 25019ecd..569fe8f4 100644
--- a/terminal.h
+++ b/terminal.h
@@ -722,8 +722,9 @@ struct config;
struct terminal *term_init(
const struct config *conf, struct fdm *fdm, struct reaper *reaper,
struct wayland *wayl, const char *foot_exe, const char *cwd,
- const char *token, int argc, char *const *argv, char *const *envp,
- void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data);
+ const char *token, const char *pty_path, int argc, char *const *argv,
+ char *const *envp, void (*shutdown_cb)(void *data, int exit_code),
+ void *shutdown_data);
bool term_shutdown(struct terminal *term);
int term_destroy(struct terminal *term);
--
2.41.0