From f2273adbcbb5a23ff58057614c389ec404c19265 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Thu, 14 Sep 2023 18:28:16 +0200 Subject: [PATCH] ENHANCED: Support pty top-level communication instead of TCP Add the ability for top-level buffers to communicate with their backing threads via pty instead of a local TCP connection. This is controlled by a new user option, enabled by default on systems where Emacs can creates a pty. * sweep.pl (sweep_top_level_start_pty/2): New predicate. * sweeprolog.el (sweeprolog-top-level-use-pty): New user option. (sweeprolog-top-level-buffer): Use it. * sweep.texi (The Prolog Top-level): Document it. --- sweep.pl | 13 ++++++++++++- sweep.texi | 44 +++++++++++++++++++++++++++++++------------- sweeprolog.el | 31 ++++++++++++++++++++++--------- 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/sweep.pl b/sweep.pl index d9d10e9..efffccb 100644 --- a/sweep.pl +++ b/sweep.pl @@ -95,7 +95,8 @@ sweep_functors_collection/2, sweep_compound_functors_collection/2, sweep_term_variable_names/2, - sweep_goal_may_cut/2 + sweep_goal_may_cut/2, + sweep_top_level_start_pty/2 ]). :- use_module(library(pldoc)). @@ -772,6 +773,16 @@ write_sweep_module_location :- format('M ~w~n', Path). :- endif. +sweep_top_level_start_pty([Name|Buffer], _) :- + thread_create(sweep_top_level_pty_client(Name), T, [detached(true)]), + thread_property(T, id(Id)), + asserta(sweep_top_level_thread_buffer(Id, Buffer)). + +sweep_top_level_pty_client(Name) :- + open(Name, read, InStream, [eof_action(reset)]), + open(Name, write, OutStream), + sweep_top_level_client(InStream, OutStream, ip(127,0,0,1)). + sweep_top_level_server(_, Port) :- tcp_socket(ServerSocket), tcp_setopt(ServerSocket, reuseaddr), diff --git a/sweep.texi b/sweep.texi index f8d4274..4aa657d 100644 --- a/sweep.texi +++ b/sweep.texi @@ -2631,19 +2631,37 @@ top-level buffer inherits the features present in other @code{comint-mode} derivatives, most of which are described in @ref{Shell Mode,,,emacs,}. -The top-level buffer is connected to a Prolog thread running in the -same process as Emacs and the main Prolog runtime. In the current -implementation, top-level buffers communicate with their corresponding -threads via local TCP connections. On the first invocation of -@code{sweeprolog-top-level}, Sweep creates a TCP server socket bound -to a random port to accept incoming connections from top-level -buffers. The TCP server only accepts connections from the local -machine, but note that other users on the same host may be able to -connect to the TCP server socket and get a Prolog top-level. This may -pose a security problem when sharing a host with untrusted users, -hence @code{sweeprolog-top-level} should not be used on shared -machines. This is the only Sweep command that you want to avoid in -such cases. +@cindex top-level communication +@cindex communication, top-level +Top-level buffers are backed by Prolog threads that run in the same +process as Emacs and the main Prolog runtime. On Unix systems, +top-levels communicate with their corresponding threads via a +pseudo-terminal device (@dfn{pty}). Alternatively, Sweep top-level +buffers can communicate with their threads via a local TCP connection. +You can force Sweep to use TCP instead of a pty on Unix systems by +customizing the user option @code{sweeprolog-top-level-use-pty} to +@code{nil}. + +@defopt sweeprolog-top-level-use-pty +Whether to use pty for top-level communication. If this is non-nil, +Sweep top-level buffers communicate with their top-level threads via a +pty, otherwise they use a local TCP connection. +@end defopt + +@code{sweeprolog-top-level-use-pty} is on by default on systems where +Emacs can use a pty. On other systems, such as MS Windows, or when +otherwise @code{sweeprolog-top-level-use-pty} is set to @code{nil}, +Sweep creates a TCP server socket bound to a random port to accept +incoming connections from top-level buffers. Sweep only starts this +TCP server socket when you first invoke of +@code{sweeprolog-top-level}, so there are no listening sockets before +you actually use the top-level. The TCP server only accepts +connections from the local machine, but note that other users on the +same host might be able to connect to the TCP server socket and get a +Prolog top-level. This may be a security concern if you are sharing a +host with untrusted users, so you should be careful about using +@code{sweeprolog-top-level} with @code{sweeprolog-top-level-use-pty} +set to @code{nil} on shared machines. @menu * Multiple Top-levels:: Creating and handling multiple Prolog top-level buffers diff --git a/sweeprolog.el b/sweeprolog.el index 3941d81..1c6d172 100644 --- a/sweeprolog.el +++ b/sweeprolog.el @@ -3131,6 +3131,12 @@ function with PROC and MSG." #'sweeprolog-top-level-sentinel) (add-hook 'kill-buffer-hook #'comint-write-input-ring nil t))) +(defcustom sweeprolog-top-level-use-pty + (not (memq system-type '(ms-dos windows-nt))) + "Whether to communicate with top-levels using pseudo-terminal (\"pty\"). + +By default, this is t on systems where Emacs can use a pty.") + (defun sweeprolog-top-level-buffer (&optional name) "Return a Prolog top-level buffer named NAME. @@ -3138,20 +3144,27 @@ If NAME is nil, use the default name \"*sweeprolog-top-level*\". If the buffer already exists, ensure it is associated with a live top-level." - (unless sweeprolog-prolog-server-port - (sweeprolog-start-prolog-server)) (let ((buf (get-buffer-create (or name "*sweeprolog-top-level*")))) (unless (process-live-p (get-buffer-process buf)) (with-current-buffer buf (unless (derived-mode-p 'sweeprolog-top-level-mode) (sweeprolog-top-level-mode))) - (unless (sweeprolog--query-once "sweep" "sweep_accept_top_level_client" - (buffer-name buf)) - (error "Failed to create new top-level!")) - (make-comint-in-buffer "sweeprolog-top-level" - buf - (cons "localhost" - sweeprolog-prolog-server-port)) + (if sweeprolog-top-level-use-pty + (progn + (make-comint-in-buffer "sweeprolog-top-level" buf nil) + (process-send-eof (get-buffer-process buf)) + (sweeprolog--query-once "sweep" "sweep_top_level_start_pty" + (cons (process-tty-name + (get-buffer-process buf)) + (buffer-name buf)))) + (unless sweeprolog-prolog-server-port + (sweeprolog-start-prolog-server)) + (sweeprolog--query-once "sweep" "sweep_accept_top_level_client" + (buffer-name buf)) + (make-comint-in-buffer "sweeprolog-top-level" + buf + (cons "localhost" + sweeprolog-prolog-server-port))) (unless comint-last-prompt (accept-process-output (get-buffer-process buf) 1)) (sweeprolog-top-level-setup-history buf) -- 2.39.2