diff options
| author | Jim Porter | 2022-07-17 20:25:00 -0700 |
|---|---|---|
| committer | Jim Porter | 2022-08-05 17:58:54 -0700 |
| commit | d7b89ea4077d4fe677ba0577245328819ee79cdc (patch) | |
| tree | d4e499042bdf2f301be7f2d7ec05f0d1bfd8d44b | |
| parent | b70369c557efed3dcd86dc64a2e73e3480dea6af (diff) | |
| download | emacs-d7b89ea4077d4fe677ba0577245328819ee79cdc.tar.gz emacs-d7b89ea4077d4fe677ba0577245328819ee79cdc.zip | |
Allow creating processes where only one of stdin or stdout is a PTY
* src/lisp.h (emacs_spawn):
* src/callproc.c (emacs_spawn): Add PTY_IN and PTY_OUT arguments to
specify which streams should be set up as a PTY.
(call_process): Adjust call to 'emacs_spawn'.
* src/process.h (Lisp_Process): Replace 'pty_flag' with 'pty_in' and
'pty_out'.
* src/process.c (is_pty_from_symbol): New function.
(make-process): Allow :connection-type to be a cons cell, and allow
using a stderr process with a PTY for stdin/stdout.
(create_process): Handle creating a process where only one of stdin or
stdout is a PTY.
* lisp/eshell/esh-proc.el (eshell-needs-pipe, eshell-needs-pipe-p):
Remove.
(eshell-gather-process-output): Use 'make-process' and set
':connection-type' as needed by the value of 'eshell-in-pipeline-p'.
* lisp/net/tramp.el (tramp-handle-make-process):
* lisp/net/tramp-adb.el (tramp-adb-handle-make-process):
* lisp/net/tramp-sh.el (tramp-sh-handle-make-process): Don't signal an
error when ':connection-type' is a cons cell.
* test/src/process-tests.el
(process-test-sentinel-wait-function-working-p): Allow passing PROC
in, and rework into...
(process-test-wait-for-sentinel): ... this.
(process-test-sentinel-accept-process-output)
(process-test-sentinel-sit-for, process-test-quoted-batfile)
(process-test-stderr-filter): Use 'process-test-wait-for-sentinel'.
(make/process/test-connection-type): New function.
(make-process/connection-type/pty, make-process/connection-type/pty-2)
(make-process/connection-type/pipe)
(make-process/connection-type/pipe-2)
(make-process/connection-type/in-pty)
(make-process/connection-type/out-pty)
(make-process/connection-type/pty-with-stderr-buffer)
(make-process/connection-type/out-pty-with-stderr-buffer): New tests.
* test/lisp/eshell/esh-proc-tests.el (esh-proc-test--detect-pty-cmd):
New variable.
(esh-proc-test/pipeline-connection-type/no-pipeline)
(esh-proc-test/pipeline-connection-type/first)
(esh-proc-test/pipeline-connection-type/middle)
(esh-proc-test/pipeline-connection-type/last): New tests.
* doc/lispref/processes.texi (Asynchronous Processes): Document new
':connection-type' behavior.
(Output from Processes): Remove caveat about ':stderr' forcing
'make-process' to use pipes.
* etc/NEWS: Announce this change (bug#56025).
| -rw-r--r-- | doc/lispref/processes.texi | 28 | ||||
| -rw-r--r-- | etc/NEWS | 12 | ||||
| -rw-r--r-- | lisp/eshell/esh-proc.el | 55 | ||||
| -rw-r--r-- | lisp/net/tramp-adb.el | 5 | ||||
| -rw-r--r-- | lisp/net/tramp-sh.el | 5 | ||||
| -rw-r--r-- | lisp/net/tramp.el | 5 | ||||
| -rw-r--r-- | src/callproc.c | 37 | ||||
| -rw-r--r-- | src/lisp.h | 3 | ||||
| -rw-r--r-- | src/process.c | 129 | ||||
| -rw-r--r-- | src/process.h | 5 | ||||
| -rw-r--r-- | test/lisp/eshell/esh-proc-tests.el | 43 | ||||
| -rw-r--r-- | test/src/process-tests.el | 121 |
12 files changed, 288 insertions, 160 deletions
diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi index 1ef8fc3d03a..e253ab9de03 100644 --- a/doc/lispref/processes.texi +++ b/doc/lispref/processes.texi | |||
| @@ -705,12 +705,13 @@ coding system will apply. @xref{Default Coding Systems}. | |||
| 705 | Initialize the type of device used to communicate with the subprocess. | 705 | Initialize the type of device used to communicate with the subprocess. |
| 706 | Possible values are @code{pty} to use a pty, @code{pipe} to use a | 706 | Possible values are @code{pty} to use a pty, @code{pipe} to use a |
| 707 | pipe, or @code{nil} to use the default derived from the value of the | 707 | pipe, or @code{nil} to use the default derived from the value of the |
| 708 | @code{process-connection-type} variable. This parameter and the value | 708 | @code{process-connection-type} variable. If @var{type} is a cons cell |
| 709 | of @code{process-connection-type} are ignored if a non-@code{nil} | 709 | @w{@code{(@var{input} . @var{output})}}, then @var{input} will be used |
| 710 | value is specified for the @code{:stderr} parameter; in that case, the | 710 | for standard input and @var{output} for standard output (and standard |
| 711 | type will always be @code{pipe}. On systems where ptys are not | 711 | error if @code{:stderr} is @code{nil}). |
| 712 | available (MS-Windows), this parameter is likewise ignored, and pipes | 712 | |
| 713 | are used unconditionally. | 713 | On systems where ptys are not available (MS-Windows), this parameter |
| 714 | is ignored, and pipes are used unconditionally. | ||
| 714 | 715 | ||
| 715 | @item :noquery @var{query-flag} | 716 | @item :noquery @var{query-flag} |
| 716 | Initialize the process query flag to @var{query-flag}. | 717 | Initialize the process query flag to @var{query-flag}. |
| @@ -1530,20 +1531,11 @@ a buffer, which is called the associated buffer of the process | |||
| 1530 | default filter discards the output. | 1531 | default filter discards the output. |
| 1531 | 1532 | ||
| 1532 | If the subprocess writes to its standard error stream, by default | 1533 | If the subprocess writes to its standard error stream, by default |
| 1533 | the error output is also passed to the process filter function. If | 1534 | the error output is also passed to the process filter function. |
| 1534 | Emacs uses a pseudo-TTY (pty) for communication with the subprocess, | 1535 | Alternatively, you could use the @code{:stderr} parameter with a |
| 1535 | then it is impossible to separate the standard output and standard | ||
| 1536 | error streams of the subprocess, because a pseudo-TTY has only one | ||
| 1537 | output channel. In that case, if you want to keep the output to those | ||
| 1538 | streams separate, you should redirect one of them to a file---for | ||
| 1539 | example, by using an appropriate shell command via | ||
| 1540 | @code{start-process-shell-command} or a similar function. | ||
| 1541 | |||
| 1542 | Alternatively, you could use the @code{:stderr} parameter with a | ||
| 1543 | non-@code{nil} value in a call to @code{make-process} | 1536 | non-@code{nil} value in a call to @code{make-process} |
| 1544 | (@pxref{Asynchronous Processes, make-process}) to make the destination | 1537 | (@pxref{Asynchronous Processes, make-process}) to make the destination |
| 1545 | of the error output separate from the standard output; in that case, | 1538 | of the error output separate from the standard output. |
| 1546 | Emacs will use pipes for communicating with the subprocess. | ||
| 1547 | 1539 | ||
| 1548 | When a subprocess terminates, Emacs reads any pending output, | 1540 | When a subprocess terminates, Emacs reads any pending output, |
| 1549 | then stops reading output from that subprocess. Therefore, if the | 1541 | then stops reading output from that subprocess. Therefore, if the |
| @@ -2332,6 +2332,12 @@ they will still be escaped, so the '.foo' symbol is still printed as | |||
| 2332 | and remapping parent of basic faces does not work reliably. | 2332 | and remapping parent of basic faces does not work reliably. |
| 2333 | Instead of remapping 'mode-line', you have to remap 'mode-line-active'. | 2333 | Instead of remapping 'mode-line', you have to remap 'mode-line-active'. |
| 2334 | 2334 | ||
| 2335 | +++ | ||
| 2336 | ** 'make-process' has been extended to support ptys when ':stderr' is set. | ||
| 2337 | Previously, setting ':stderr' to a non-nil value would force the | ||
| 2338 | process's connection to use pipes. Now, Emacs will use a pty for | ||
| 2339 | stdin and stdout if requested no matter the value of ':stderr'. | ||
| 2340 | |||
| 2335 | --- | 2341 | --- |
| 2336 | ** User option 'mail-source-ignore-errors' is now obsolete. | 2342 | ** User option 'mail-source-ignore-errors' is now obsolete. |
| 2337 | The whole mechanism for prompting users to continue in case of | 2343 | The whole mechanism for prompting users to continue in case of |
| @@ -3324,6 +3330,12 @@ This is useful when quoting shell arguments for a remote shell | |||
| 3324 | invocation. Such shells are POSIX conformant by default. | 3330 | invocation. Such shells are POSIX conformant by default. |
| 3325 | 3331 | ||
| 3326 | +++ | 3332 | +++ |
| 3333 | ** 'make-process' can set connection type independently for input and output. | ||
| 3334 | When calling 'make-process', communication via pty can be enabled | ||
| 3335 | selectively for just input or output by passing a cons cell for | ||
| 3336 | ':connection-type', e.g. '(pipe . pty)'. | ||
| 3337 | |||
| 3338 | +++ | ||
| 3327 | ** 'signal-process' now consults the list 'signal-process-functions'. | 3339 | ** 'signal-process' now consults the list 'signal-process-functions'. |
| 3328 | This is to determine which function has to be called in order to | 3340 | This is to determine which function has to be called in order to |
| 3329 | deliver the signal. This allows Tramp to send the signal to remote | 3341 | deliver the signal. This allows Tramp to send the signal to remote |
diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el index 70426ccaf2a..99b43661f2c 100644 --- a/lisp/eshell/esh-proc.el +++ b/lisp/eshell/esh-proc.el | |||
| @@ -250,30 +250,6 @@ The prompt will be set to PROMPT." | |||
| 250 | "A marker that tracks the beginning of output of the last subprocess. | 250 | "A marker that tracks the beginning of output of the last subprocess. |
| 251 | Used only on systems which do not support async subprocesses.") | 251 | Used only on systems which do not support async subprocesses.") |
| 252 | 252 | ||
| 253 | (defvar eshell-needs-pipe | ||
| 254 | '("bc" | ||
| 255 | ;; xclip.el (in GNU ELPA) calls all of these with | ||
| 256 | ;; `process-connection-type' set to nil. | ||
| 257 | "pbpaste" "putclip" "xclip" "xsel" "wl-copy") | ||
| 258 | "List of commands which need `process-connection-type' to be nil. | ||
| 259 | Currently only affects commands in pipelines, and not those at | ||
| 260 | the front. If an element contains a directory part it must match | ||
| 261 | the full name of a command, otherwise just the nondirectory part must match.") | ||
| 262 | |||
| 263 | (defun eshell-needs-pipe-p (command) | ||
| 264 | "Return non-nil if COMMAND needs `process-connection-type' to be nil. | ||
| 265 | See `eshell-needs-pipe'." | ||
| 266 | (and (bound-and-true-p eshell-in-pipeline-p) | ||
| 267 | (not (eq eshell-in-pipeline-p 'first)) | ||
| 268 | ;; FIXME should this return non-nil for anything that is | ||
| 269 | ;; neither 'first nor 'last? See bug#1388 discussion. | ||
| 270 | (catch 'found | ||
| 271 | (dolist (exe eshell-needs-pipe) | ||
| 272 | (if (string-equal exe (if (string-search "/" exe) | ||
| 273 | command | ||
| 274 | (file-name-nondirectory command))) | ||
| 275 | (throw 'found t)))))) | ||
| 276 | |||
| 277 | (defun eshell-gather-process-output (command args) | 253 | (defun eshell-gather-process-output (command args) |
| 278 | "Gather the output from COMMAND + ARGS." | 254 | "Gather the output from COMMAND + ARGS." |
| 279 | (require 'esh-var) | 255 | (require 'esh-var) |
| @@ -290,31 +266,36 @@ See `eshell-needs-pipe'." | |||
| 290 | (cond | 266 | (cond |
| 291 | ((fboundp 'make-process) | 267 | ((fboundp 'make-process) |
| 292 | (setq proc | 268 | (setq proc |
| 293 | (let ((process-connection-type | 269 | (let ((command (file-local-name (expand-file-name command))) |
| 294 | (unless (eshell-needs-pipe-p command) | 270 | (conn-type (pcase (bound-and-true-p eshell-in-pipeline-p) |
| 295 | process-connection-type)) | 271 | ('first '(nil . pipe)) |
| 296 | (command (file-local-name (expand-file-name command)))) | 272 | ('last '(pipe . nil)) |
| 297 | (apply #'start-file-process | 273 | ('t 'pipe) |
| 298 | (file-name-nondirectory command) nil command args))) | 274 | ('nil nil)))) |
| 275 | (make-process | ||
| 276 | :name (file-name-nondirectory command) | ||
| 277 | :buffer (current-buffer) | ||
| 278 | :command (cons command args) | ||
| 279 | :filter (if (eshell-interactive-output-p) | ||
| 280 | #'eshell-output-filter | ||
| 281 | #'eshell-insertion-filter) | ||
| 282 | :sentinel #'eshell-sentinel | ||
| 283 | :connection-type conn-type | ||
| 284 | :file-handler t))) | ||
| 299 | (eshell-record-process-object proc) | 285 | (eshell-record-process-object proc) |
| 300 | (set-process-buffer proc (current-buffer)) | ||
| 301 | (set-process-filter proc (if (eshell-interactive-output-p) | ||
| 302 | #'eshell-output-filter | ||
| 303 | #'eshell-insertion-filter)) | ||
| 304 | (set-process-sentinel proc #'eshell-sentinel) | ||
| 305 | (run-hook-with-args 'eshell-exec-hook proc) | 286 | (run-hook-with-args 'eshell-exec-hook proc) |
| 306 | (when (fboundp 'process-coding-system) | 287 | (when (fboundp 'process-coding-system) |
| 307 | (let ((coding-systems (process-coding-system proc))) | 288 | (let ((coding-systems (process-coding-system proc))) |
| 308 | (setq decoding (car coding-systems) | 289 | (setq decoding (car coding-systems) |
| 309 | encoding (cdr coding-systems))) | 290 | encoding (cdr coding-systems))) |
| 310 | ;; If start-process decided to use some coding system for | 291 | ;; If `make-process' decided to use some coding system for |
| 311 | ;; decoding data sent from the process and the coding system | 292 | ;; decoding data sent from the process and the coding system |
| 312 | ;; doesn't specify EOL conversion, we had better convert CRLF | 293 | ;; doesn't specify EOL conversion, we had better convert CRLF |
| 313 | ;; to LF. | 294 | ;; to LF. |
| 314 | (if (vectorp (coding-system-eol-type decoding)) | 295 | (if (vectorp (coding-system-eol-type decoding)) |
| 315 | (setq decoding (coding-system-change-eol-conversion decoding 'dos) | 296 | (setq decoding (coding-system-change-eol-conversion decoding 'dos) |
| 316 | changed t)) | 297 | changed t)) |
| 317 | ;; Even if start-process left the coding system for encoding | 298 | ;; Even if `make-process' left the coding system for encoding |
| 318 | ;; data sent from the process undecided, we had better use the | 299 | ;; data sent from the process undecided, we had better use the |
| 319 | ;; same one as what we use for decoding. But, we should | 300 | ;; same one as what we use for decoding. But, we should |
| 320 | ;; suppress EOL conversion. | 301 | ;; suppress EOL conversion. |
diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index ef0cc2d66c6..918de68ea9b 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el | |||
| @@ -877,7 +877,10 @@ implementation will be used." | |||
| 877 | (signal 'wrong-type-argument (list #'symbolp coding))) | 877 | (signal 'wrong-type-argument (list #'symbolp coding))) |
| 878 | (when (eq connection-type t) | 878 | (when (eq connection-type t) |
| 879 | (setq connection-type 'pty)) | 879 | (setq connection-type 'pty)) |
| 880 | (unless (memq connection-type '(nil pipe pty)) | 880 | (unless (or (and (consp connection-type) |
| 881 | (memq (car connection-type) '(nil pipe pty)) | ||
| 882 | (memq (cdr connection-type) '(nil pipe pty))) | ||
| 883 | (memq connection-type '(nil pipe pty))) | ||
| 881 | (signal 'wrong-type-argument (list #'symbolp connection-type))) | 884 | (signal 'wrong-type-argument (list #'symbolp connection-type))) |
| 882 | (unless (or (null filter) (eq filter t) (functionp filter)) | 885 | (unless (or (null filter) (eq filter t) (functionp filter)) |
| 883 | (signal 'wrong-type-argument (list #'functionp filter))) | 886 | (signal 'wrong-type-argument (list #'functionp filter))) |
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 9e5347252ad..38fffadd4ec 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el | |||
| @@ -2842,7 +2842,10 @@ implementation will be used." | |||
| 2842 | (signal 'wrong-type-argument (list #'symbolp coding))) | 2842 | (signal 'wrong-type-argument (list #'symbolp coding))) |
| 2843 | (when (eq connection-type t) | 2843 | (when (eq connection-type t) |
| 2844 | (setq connection-type 'pty)) | 2844 | (setq connection-type 'pty)) |
| 2845 | (unless (memq connection-type '(nil pipe pty)) | 2845 | (unless (or (and (consp connection-type) |
| 2846 | (memq (car connection-type) '(nil pipe pty)) | ||
| 2847 | (memq (cdr connection-type) '(nil pipe pty))) | ||
| 2848 | (memq connection-type '(nil pipe pty))) | ||
| 2846 | (signal 'wrong-type-argument (list #'symbolp connection-type))) | 2849 | (signal 'wrong-type-argument (list #'symbolp connection-type))) |
| 2847 | (unless (or (null filter) (eq filter t) (functionp filter)) | 2850 | (unless (or (null filter) (eq filter t) (functionp filter)) |
| 2848 | (signal 'wrong-type-argument (list #'functionp filter))) | 2851 | (signal 'wrong-type-argument (list #'functionp filter))) |
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index dcc8c632f91..ae31287eced 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el | |||
| @@ -4708,7 +4708,10 @@ substitution. SPEC-LIST is a list of char/value pairs used for | |||
| 4708 | (signal 'wrong-type-argument (list #'symbolp coding))) | 4708 | (signal 'wrong-type-argument (list #'symbolp coding))) |
| 4709 | (when (eq connection-type t) | 4709 | (when (eq connection-type t) |
| 4710 | (setq connection-type 'pty)) | 4710 | (setq connection-type 'pty)) |
| 4711 | (unless (memq connection-type '(nil pipe pty)) | 4711 | (unless (or (and (consp connection-type) |
| 4712 | (memq (car connection-type) '(nil pipe pty)) | ||
| 4713 | (memq (cdr connection-type) '(nil pipe pty))) | ||
| 4714 | (memq connection-type '(nil pipe pty))) | ||
| 4712 | (signal 'wrong-type-argument (list #'symbolp connection-type))) | 4715 | (signal 'wrong-type-argument (list #'symbolp connection-type))) |
| 4713 | (unless (or (null filter) (eq filter t) (functionp filter)) | 4716 | (unless (or (null filter) (eq filter t) (functionp filter)) |
| 4714 | (signal 'wrong-type-argument (list #'functionp filter))) | 4717 | (signal 'wrong-type-argument (list #'functionp filter))) |
diff --git a/src/callproc.c b/src/callproc.c index dd162f36a6c..aec0a2f5a58 100644 --- a/src/callproc.c +++ b/src/callproc.c | |||
| @@ -650,7 +650,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, | |||
| 650 | 650 | ||
| 651 | child_errno | 651 | child_errno |
| 652 | = emacs_spawn (&pid, filefd, fd_output, fd_error, new_argv, env, | 652 | = emacs_spawn (&pid, filefd, fd_output, fd_error, new_argv, env, |
| 653 | SSDATA (current_dir), NULL, &oldset); | 653 | SSDATA (current_dir), NULL, false, false, &oldset); |
| 654 | eassert ((child_errno == 0) == (0 < pid)); | 654 | eassert ((child_errno == 0) == (0 < pid)); |
| 655 | 655 | ||
| 656 | if (pid > 0) | 656 | if (pid > 0) |
| @@ -1412,14 +1412,15 @@ emacs_posix_spawn_init_attributes (posix_spawnattr_t *attributes, | |||
| 1412 | int | 1412 | int |
| 1413 | emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, | 1413 | emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, |
| 1414 | char **argv, char **envp, const char *cwd, | 1414 | char **argv, char **envp, const char *cwd, |
| 1415 | const char *pty, const sigset_t *oldset) | 1415 | const char *pty_name, bool pty_in, bool pty_out, |
| 1416 | const sigset_t *oldset) | ||
| 1416 | { | 1417 | { |
| 1417 | #if USABLE_POSIX_SPAWN | 1418 | #if USABLE_POSIX_SPAWN |
| 1418 | /* Prefer the simpler `posix_spawn' if available. `posix_spawn' | 1419 | /* Prefer the simpler `posix_spawn' if available. `posix_spawn' |
| 1419 | doesn't yet support setting up pseudoterminals, so we fall back | 1420 | doesn't yet support setting up pseudoterminals, so we fall back |
| 1420 | to `vfork' if we're supposed to use a pseudoterminal. */ | 1421 | to `vfork' if we're supposed to use a pseudoterminal. */ |
| 1421 | 1422 | ||
| 1422 | bool use_posix_spawn = pty == NULL; | 1423 | bool use_posix_spawn = pty_name == NULL; |
| 1423 | 1424 | ||
| 1424 | posix_spawn_file_actions_t actions; | 1425 | posix_spawn_file_actions_t actions; |
| 1425 | posix_spawnattr_t attributes; | 1426 | posix_spawnattr_t attributes; |
| @@ -1473,7 +1474,9 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, | |||
| 1473 | /* vfork, and prevent local vars from being clobbered by the vfork. */ | 1474 | /* vfork, and prevent local vars from being clobbered by the vfork. */ |
| 1474 | pid_t *volatile newpid_volatile = newpid; | 1475 | pid_t *volatile newpid_volatile = newpid; |
| 1475 | const char *volatile cwd_volatile = cwd; | 1476 | const char *volatile cwd_volatile = cwd; |
| 1476 | const char *volatile pty_volatile = pty; | 1477 | const char *volatile ptyname_volatile = pty_name; |
| 1478 | bool volatile ptyin_volatile = pty_in; | ||
| 1479 | bool volatile ptyout_volatile = pty_out; | ||
| 1477 | char **volatile argv_volatile = argv; | 1480 | char **volatile argv_volatile = argv; |
| 1478 | int volatile stdin_volatile = std_in; | 1481 | int volatile stdin_volatile = std_in; |
| 1479 | int volatile stdout_volatile = std_out; | 1482 | int volatile stdout_volatile = std_out; |
| @@ -1495,7 +1498,9 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, | |||
| 1495 | 1498 | ||
| 1496 | newpid = newpid_volatile; | 1499 | newpid = newpid_volatile; |
| 1497 | cwd = cwd_volatile; | 1500 | cwd = cwd_volatile; |
| 1498 | pty = pty_volatile; | 1501 | pty_name = ptyname_volatile; |
| 1502 | pty_in = ptyin_volatile; | ||
| 1503 | pty_out = ptyout_volatile; | ||
| 1499 | argv = argv_volatile; | 1504 | argv = argv_volatile; |
| 1500 | std_in = stdin_volatile; | 1505 | std_in = stdin_volatile; |
| 1501 | std_out = stdout_volatile; | 1506 | std_out = stdout_volatile; |
| @@ -1506,13 +1511,12 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, | |||
| 1506 | if (pid == 0) | 1511 | if (pid == 0) |
| 1507 | #endif /* not WINDOWSNT */ | 1512 | #endif /* not WINDOWSNT */ |
| 1508 | { | 1513 | { |
| 1509 | bool pty_flag = pty != NULL; | ||
| 1510 | /* Make the pty be the controlling terminal of the process. */ | 1514 | /* Make the pty be the controlling terminal of the process. */ |
| 1511 | #ifdef HAVE_PTYS | 1515 | #ifdef HAVE_PTYS |
| 1512 | dissociate_controlling_tty (); | 1516 | dissociate_controlling_tty (); |
| 1513 | 1517 | ||
| 1514 | /* Make the pty's terminal the controlling terminal. */ | 1518 | /* Make the pty's terminal the controlling terminal. */ |
| 1515 | if (pty_flag && std_in >= 0) | 1519 | if (pty_in && std_in >= 0) |
| 1516 | { | 1520 | { |
| 1517 | #ifdef TIOCSCTTY | 1521 | #ifdef TIOCSCTTY |
| 1518 | /* We ignore the return value | 1522 | /* We ignore the return value |
| @@ -1521,7 +1525,7 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, | |||
| 1521 | #endif | 1525 | #endif |
| 1522 | } | 1526 | } |
| 1523 | #if defined (LDISC1) | 1527 | #if defined (LDISC1) |
| 1524 | if (pty_flag && std_in >= 0) | 1528 | if (pty_in && std_in >= 0) |
| 1525 | { | 1529 | { |
| 1526 | struct termios t; | 1530 | struct termios t; |
| 1527 | tcgetattr (std_in, &t); | 1531 | tcgetattr (std_in, &t); |
| @@ -1531,7 +1535,7 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, | |||
| 1531 | } | 1535 | } |
| 1532 | #else | 1536 | #else |
| 1533 | #if defined (NTTYDISC) && defined (TIOCSETD) | 1537 | #if defined (NTTYDISC) && defined (TIOCSETD) |
| 1534 | if (pty_flag && std_in >= 0) | 1538 | if (pty_in && std_in >= 0) |
| 1535 | { | 1539 | { |
| 1536 | /* Use new line discipline. */ | 1540 | /* Use new line discipline. */ |
| 1537 | int ldisc = NTTYDISC; | 1541 | int ldisc = NTTYDISC; |
| @@ -1548,18 +1552,21 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, | |||
| 1548 | both TIOCSCTTY is defined. */ | 1552 | both TIOCSCTTY is defined. */ |
| 1549 | /* Now close the pty (if we had it open) and reopen it. | 1553 | /* Now close the pty (if we had it open) and reopen it. |
| 1550 | This makes the pty the controlling terminal of the subprocess. */ | 1554 | This makes the pty the controlling terminal of the subprocess. */ |
| 1551 | if (pty_flag) | 1555 | if (pty_name) |
| 1552 | { | 1556 | { |
| 1553 | 1557 | ||
| 1554 | /* I wonder if emacs_close (emacs_open (pty, ...)) | 1558 | /* I wonder if emacs_close (emacs_open (pty, ...)) |
| 1555 | would work? */ | 1559 | would work? */ |
| 1556 | if (std_in >= 0) | 1560 | if (pty_in && std_in >= 0) |
| 1557 | emacs_close (std_in); | 1561 | emacs_close (std_in); |
| 1558 | std_out = std_in = emacs_open_noquit (pty, O_RDWR, 0); | 1562 | int ptyfd = emacs_open_noquit (pty_name, O_RDWR, 0); |
| 1559 | 1563 | if (pty_in) | |
| 1564 | std_in = ptyfd; | ||
| 1565 | if (pty_out) | ||
| 1566 | std_out = ptyfd; | ||
| 1560 | if (std_in < 0) | 1567 | if (std_in < 0) |
| 1561 | { | 1568 | { |
| 1562 | emacs_perror (pty); | 1569 | emacs_perror (pty_name); |
| 1563 | _exit (EXIT_CANCELED); | 1570 | _exit (EXIT_CANCELED); |
| 1564 | } | 1571 | } |
| 1565 | 1572 | ||
| @@ -1599,7 +1606,7 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, | |||
| 1599 | /* Stop blocking SIGCHLD in the child. */ | 1606 | /* Stop blocking SIGCHLD in the child. */ |
| 1600 | unblock_child_signal (oldset); | 1607 | unblock_child_signal (oldset); |
| 1601 | 1608 | ||
| 1602 | if (pty_flag) | 1609 | if (pty_out) |
| 1603 | child_setup_tty (std_out); | 1610 | child_setup_tty (std_out); |
| 1604 | #endif | 1611 | #endif |
| 1605 | 1612 | ||
diff --git a/src/lisp.h b/src/lisp.h index 8e36620fe53..fe6e98843d1 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4943,7 +4943,8 @@ extern void setup_process_coding_systems (Lisp_Object); | |||
| 4943 | #endif | 4943 | #endif |
| 4944 | 4944 | ||
| 4945 | extern int emacs_spawn (pid_t *, int, int, int, char **, char **, | 4945 | extern int emacs_spawn (pid_t *, int, int, int, char **, char **, |
| 4946 | const char *, const char *, const sigset_t *); | 4946 | const char *, const char *, bool, bool, |
| 4947 | const sigset_t *); | ||
| 4947 | extern char **make_environment_block (Lisp_Object) ATTRIBUTE_RETURNS_NONNULL; | 4948 | extern char **make_environment_block (Lisp_Object) ATTRIBUTE_RETURNS_NONNULL; |
| 4948 | extern void init_callproc_1 (void); | 4949 | extern void init_callproc_1 (void); |
| 4949 | extern void init_callproc (void); | 4950 | extern void init_callproc (void); |
diff --git a/src/process.c b/src/process.c index 1ac5a509e56..68dbd8b68bd 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -1316,6 +1316,19 @@ set_process_filter_masks (struct Lisp_Process *p) | |||
| 1316 | add_process_read_fd (p->infd); | 1316 | add_process_read_fd (p->infd); |
| 1317 | } | 1317 | } |
| 1318 | 1318 | ||
| 1319 | static bool | ||
| 1320 | is_pty_from_symbol (Lisp_Object symbol) | ||
| 1321 | { | ||
| 1322 | if (EQ (symbol, Qpty)) | ||
| 1323 | return true; | ||
| 1324 | else if (EQ (symbol, Qpipe)) | ||
| 1325 | return false; | ||
| 1326 | else if (NILP (symbol)) | ||
| 1327 | return !NILP (Vprocess_connection_type); | ||
| 1328 | else | ||
| 1329 | report_file_error ("Unknown connection type", symbol); | ||
| 1330 | } | ||
| 1331 | |||
| 1319 | DEFUN ("set-process-filter", Fset_process_filter, Sset_process_filter, | 1332 | DEFUN ("set-process-filter", Fset_process_filter, Sset_process_filter, |
| 1320 | 2, 2, 0, | 1333 | 2, 2, 0, |
| 1321 | doc: /* Give PROCESS the filter function FILTER; nil means default. | 1334 | doc: /* Give PROCESS the filter function FILTER; nil means default. |
| @@ -1741,15 +1754,18 @@ signals to stop and continue a process. | |||
| 1741 | :connection-type TYPE -- TYPE is control type of device used to | 1754 | :connection-type TYPE -- TYPE is control type of device used to |
| 1742 | communicate with subprocesses. Values are `pipe' to use a pipe, `pty' | 1755 | communicate with subprocesses. Values are `pipe' to use a pipe, `pty' |
| 1743 | to use a pty, or nil to use the default specified through | 1756 | to use a pty, or nil to use the default specified through |
| 1744 | `process-connection-type'. | 1757 | `process-connection-type'. If TYPE is a cons (INPUT . OUTPUT), then |
| 1758 | INPUT will be used for standard input and OUTPUT for standard output | ||
| 1759 | (and standard error if `:stderr' is nil). | ||
| 1745 | 1760 | ||
| 1746 | :filter FILTER -- Install FILTER as the process filter. | 1761 | :filter FILTER -- Install FILTER as the process filter. |
| 1747 | 1762 | ||
| 1748 | :sentinel SENTINEL -- Install SENTINEL as the process sentinel. | 1763 | :sentinel SENTINEL -- Install SENTINEL as the process sentinel. |
| 1749 | 1764 | ||
| 1750 | :stderr STDERR -- STDERR is either a buffer or a pipe process attached | 1765 | :stderr STDERR -- STDERR is either a buffer or a pipe process attached |
| 1751 | to the standard error of subprocess. Specifying this implies | 1766 | to the standard error of subprocess. When specifying this, the |
| 1752 | `:connection-type' is set to `pipe'. If STDERR is nil, standard error | 1767 | subprocess's standard error will always communicate via a pipe, no |
| 1768 | matter the value of `:connection-type'. If STDERR is nil, standard error | ||
| 1753 | is mixed with standard output and sent to BUFFER or FILTER. (Note | 1769 | is mixed with standard output and sent to BUFFER or FILTER. (Note |
| 1754 | that specifying :stderr will create a new, separate (but associated) | 1770 | that specifying :stderr will create a new, separate (but associated) |
| 1755 | process, with its own filter and sentinel. See | 1771 | process, with its own filter and sentinel. See |
| @@ -1845,22 +1861,20 @@ usage: (make-process &rest ARGS) */) | |||
| 1845 | CHECK_TYPE (NILP (tem), Qnull, tem); | 1861 | CHECK_TYPE (NILP (tem), Qnull, tem); |
| 1846 | 1862 | ||
| 1847 | tem = plist_get (contact, QCconnection_type); | 1863 | tem = plist_get (contact, QCconnection_type); |
| 1848 | if (EQ (tem, Qpty)) | 1864 | if (CONSP (tem)) |
| 1849 | XPROCESS (proc)->pty_flag = true; | 1865 | { |
| 1850 | else if (EQ (tem, Qpipe)) | 1866 | XPROCESS (proc)->pty_in = is_pty_from_symbol (XCAR (tem)); |
| 1851 | XPROCESS (proc)->pty_flag = false; | 1867 | XPROCESS (proc)->pty_out = is_pty_from_symbol (XCDR (tem)); |
| 1852 | else if (NILP (tem)) | 1868 | } |
| 1853 | XPROCESS (proc)->pty_flag = !NILP (Vprocess_connection_type); | ||
| 1854 | else | 1869 | else |
| 1855 | report_file_error ("Unknown connection type", tem); | ||
| 1856 | |||
| 1857 | if (!NILP (stderrproc)) | ||
| 1858 | { | 1870 | { |
| 1859 | pset_stderrproc (XPROCESS (proc), stderrproc); | 1871 | XPROCESS (proc)->pty_in = XPROCESS (proc)->pty_out = |
| 1860 | 1872 | is_pty_from_symbol (tem); | |
| 1861 | XPROCESS (proc)->pty_flag = false; | ||
| 1862 | } | 1873 | } |
| 1863 | 1874 | ||
| 1875 | if (!NILP (stderrproc)) | ||
| 1876 | pset_stderrproc (XPROCESS (proc), stderrproc); | ||
| 1877 | |||
| 1864 | #ifdef HAVE_GNUTLS | 1878 | #ifdef HAVE_GNUTLS |
| 1865 | /* AKA GNUTLS_INITSTAGE(proc). */ | 1879 | /* AKA GNUTLS_INITSTAGE(proc). */ |
| 1866 | verify (GNUTLS_STAGE_EMPTY == 0); | 1880 | verify (GNUTLS_STAGE_EMPTY == 0); |
| @@ -2099,66 +2113,80 @@ static void | |||
| 2099 | create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) | 2113 | create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) |
| 2100 | { | 2114 | { |
| 2101 | struct Lisp_Process *p = XPROCESS (process); | 2115 | struct Lisp_Process *p = XPROCESS (process); |
| 2102 | int inchannel, outchannel; | 2116 | int inchannel = -1, outchannel = -1; |
| 2103 | pid_t pid = -1; | 2117 | pid_t pid = -1; |
| 2104 | int vfork_errno; | 2118 | int vfork_errno; |
| 2105 | int forkin, forkout, forkerr = -1; | 2119 | int forkin, forkout, forkerr = -1; |
| 2106 | bool pty_flag = 0; | 2120 | bool pty_in = false, pty_out = false; |
| 2107 | char pty_name[PTY_NAME_SIZE]; | 2121 | char pty_name[PTY_NAME_SIZE]; |
| 2108 | Lisp_Object lisp_pty_name = Qnil; | 2122 | Lisp_Object lisp_pty_name = Qnil; |
| 2123 | int ptychannel = -1, pty_tty = -1; | ||
| 2109 | sigset_t oldset; | 2124 | sigset_t oldset; |
| 2110 | 2125 | ||
| 2111 | /* Ensure that the SIGCHLD handler can notify | 2126 | /* Ensure that the SIGCHLD handler can notify |
| 2112 | `wait_reading_process_output'. */ | 2127 | `wait_reading_process_output'. */ |
| 2113 | child_signal_init (); | 2128 | child_signal_init (); |
| 2114 | 2129 | ||
| 2115 | inchannel = outchannel = -1; | 2130 | if (p->pty_in || p->pty_out) |
| 2116 | 2131 | ptychannel = allocate_pty (pty_name); | |
| 2117 | if (p->pty_flag) | ||
| 2118 | outchannel = inchannel = allocate_pty (pty_name); | ||
| 2119 | 2132 | ||
| 2120 | if (inchannel >= 0) | 2133 | if (ptychannel >= 0) |
| 2121 | { | 2134 | { |
| 2122 | p->open_fd[READ_FROM_SUBPROCESS] = inchannel; | ||
| 2123 | #if ! defined (USG) || defined (USG_SUBTTY_WORKS) | 2135 | #if ! defined (USG) || defined (USG_SUBTTY_WORKS) |
| 2124 | /* On most USG systems it does not work to open the pty's tty here, | 2136 | /* On most USG systems it does not work to open the pty's tty here, |
| 2125 | then close it and reopen it in the child. */ | 2137 | then close it and reopen it in the child. */ |
| 2126 | /* Don't let this terminal become our controlling terminal | 2138 | /* Don't let this terminal become our controlling terminal |
| 2127 | (in case we don't have one). */ | 2139 | (in case we don't have one). */ |
| 2128 | forkout = forkin = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0); | 2140 | pty_tty = emacs_open (pty_name, O_RDWR | O_NOCTTY, 0); |
| 2129 | if (forkin < 0) | 2141 | if (pty_tty < 0) |
| 2130 | report_file_error ("Opening pty", Qnil); | 2142 | report_file_error ("Opening pty", Qnil); |
| 2131 | p->open_fd[SUBPROCESS_STDIN] = forkin; | ||
| 2132 | #else | ||
| 2133 | forkin = forkout = -1; | ||
| 2134 | #endif /* not USG, or USG_SUBTTY_WORKS */ | 2143 | #endif /* not USG, or USG_SUBTTY_WORKS */ |
| 2135 | pty_flag = 1; | 2144 | pty_in = p->pty_in; |
| 2145 | pty_out = p->pty_out; | ||
| 2136 | lisp_pty_name = build_string (pty_name); | 2146 | lisp_pty_name = build_string (pty_name); |
| 2137 | } | 2147 | } |
| 2148 | |||
| 2149 | /* Set up stdin for the child process. */ | ||
| 2150 | if (ptychannel >= 0 && p->pty_in) | ||
| 2151 | { | ||
| 2152 | p->open_fd[SUBPROCESS_STDIN] = forkin = pty_tty; | ||
| 2153 | outchannel = ptychannel; | ||
| 2154 | } | ||
| 2138 | else | 2155 | else |
| 2139 | { | 2156 | { |
| 2140 | if (emacs_pipe (p->open_fd + SUBPROCESS_STDIN) != 0 | 2157 | if (emacs_pipe (p->open_fd + SUBPROCESS_STDIN) != 0) |
| 2141 | || emacs_pipe (p->open_fd + READ_FROM_SUBPROCESS) != 0) | ||
| 2142 | report_file_error ("Creating pipe", Qnil); | 2158 | report_file_error ("Creating pipe", Qnil); |
| 2143 | forkin = p->open_fd[SUBPROCESS_STDIN]; | 2159 | forkin = p->open_fd[SUBPROCESS_STDIN]; |
| 2144 | outchannel = p->open_fd[WRITE_TO_SUBPROCESS]; | 2160 | outchannel = p->open_fd[WRITE_TO_SUBPROCESS]; |
| 2161 | } | ||
| 2162 | |||
| 2163 | /* Set up stdout for the child process. */ | ||
| 2164 | if (ptychannel >= 0 && p->pty_out) | ||
| 2165 | { | ||
| 2166 | forkout = pty_tty; | ||
| 2167 | p->open_fd[READ_FROM_SUBPROCESS] = inchannel = ptychannel; | ||
| 2168 | } | ||
| 2169 | else | ||
| 2170 | { | ||
| 2171 | if (emacs_pipe (p->open_fd + READ_FROM_SUBPROCESS) != 0) | ||
| 2172 | report_file_error ("Creating pipe", Qnil); | ||
| 2145 | inchannel = p->open_fd[READ_FROM_SUBPROCESS]; | 2173 | inchannel = p->open_fd[READ_FROM_SUBPROCESS]; |
| 2146 | forkout = p->open_fd[SUBPROCESS_STDOUT]; | 2174 | forkout = p->open_fd[SUBPROCESS_STDOUT]; |
| 2147 | 2175 | ||
| 2148 | #if defined(GNU_LINUX) && defined(F_SETPIPE_SZ) | 2176 | #if defined(GNU_LINUX) && defined(F_SETPIPE_SZ) |
| 2149 | fcntl (inchannel, F_SETPIPE_SZ, read_process_output_max); | 2177 | fcntl (inchannel, F_SETPIPE_SZ, read_process_output_max); |
| 2150 | #endif | 2178 | #endif |
| 2179 | } | ||
| 2151 | 2180 | ||
| 2152 | if (!NILP (p->stderrproc)) | 2181 | if (!NILP (p->stderrproc)) |
| 2153 | { | 2182 | { |
| 2154 | struct Lisp_Process *pp = XPROCESS (p->stderrproc); | 2183 | struct Lisp_Process *pp = XPROCESS (p->stderrproc); |
| 2155 | 2184 | ||
| 2156 | forkerr = pp->open_fd[SUBPROCESS_STDOUT]; | 2185 | forkerr = pp->open_fd[SUBPROCESS_STDOUT]; |
| 2157 | 2186 | ||
| 2158 | /* Close unnecessary file descriptors. */ | 2187 | /* Close unnecessary file descriptors. */ |
| 2159 | close_process_fd (&pp->open_fd[WRITE_TO_SUBPROCESS]); | 2188 | close_process_fd (&pp->open_fd[WRITE_TO_SUBPROCESS]); |
| 2160 | close_process_fd (&pp->open_fd[SUBPROCESS_STDIN]); | 2189 | close_process_fd (&pp->open_fd[SUBPROCESS_STDIN]); |
| 2161 | } | ||
| 2162 | } | 2190 | } |
| 2163 | 2191 | ||
| 2164 | if (FD_SETSIZE <= inchannel || FD_SETSIZE <= outchannel) | 2192 | if (FD_SETSIZE <= inchannel || FD_SETSIZE <= outchannel) |
| @@ -2183,7 +2211,8 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) | |||
| 2183 | we just reopen the device (see emacs_get_tty_pgrp) as this is | 2211 | we just reopen the device (see emacs_get_tty_pgrp) as this is |
| 2184 | more portable (see USG_SUBTTY_WORKS above). */ | 2212 | more portable (see USG_SUBTTY_WORKS above). */ |
| 2185 | 2213 | ||
| 2186 | p->pty_flag = pty_flag; | 2214 | p->pty_in = pty_in; |
| 2215 | p->pty_out = pty_out; | ||
| 2187 | pset_status (p, Qrun); | 2216 | pset_status (p, Qrun); |
| 2188 | 2217 | ||
| 2189 | if (!EQ (p->command, Qt) | 2218 | if (!EQ (p->command, Qt) |
| @@ -2199,13 +2228,15 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) | |||
| 2199 | block_input (); | 2228 | block_input (); |
| 2200 | block_child_signal (&oldset); | 2229 | block_child_signal (&oldset); |
| 2201 | 2230 | ||
| 2202 | pty_flag = p->pty_flag; | 2231 | pty_in = p->pty_in; |
| 2203 | eassert (pty_flag == ! NILP (lisp_pty_name)); | 2232 | pty_out = p->pty_out; |
| 2233 | eassert ((pty_in || pty_out) == ! NILP (lisp_pty_name)); | ||
| 2204 | 2234 | ||
| 2205 | vfork_errno | 2235 | vfork_errno |
| 2206 | = emacs_spawn (&pid, forkin, forkout, forkerr, new_argv, env, | 2236 | = emacs_spawn (&pid, forkin, forkout, forkerr, new_argv, env, |
| 2207 | SSDATA (current_dir), | 2237 | SSDATA (current_dir), |
| 2208 | pty_flag ? SSDATA (lisp_pty_name) : NULL, &oldset); | 2238 | pty_in || pty_out ? SSDATA (lisp_pty_name) : NULL, |
| 2239 | pty_in, pty_out, &oldset); | ||
| 2209 | 2240 | ||
| 2210 | eassert ((vfork_errno == 0) == (0 < pid)); | 2241 | eassert ((vfork_errno == 0) == (0 < pid)); |
| 2211 | 2242 | ||
| @@ -2263,7 +2294,7 @@ create_pty (Lisp_Object process) | |||
| 2263 | { | 2294 | { |
| 2264 | struct Lisp_Process *p = XPROCESS (process); | 2295 | struct Lisp_Process *p = XPROCESS (process); |
| 2265 | char pty_name[PTY_NAME_SIZE]; | 2296 | char pty_name[PTY_NAME_SIZE]; |
| 2266 | int pty_fd = !p->pty_flag ? -1 : allocate_pty (pty_name); | 2297 | int pty_fd = !(p->pty_in || p->pty_out) ? -1 : allocate_pty (pty_name); |
| 2267 | 2298 | ||
| 2268 | if (pty_fd >= 0) | 2299 | if (pty_fd >= 0) |
| 2269 | { | 2300 | { |
| @@ -2301,7 +2332,7 @@ create_pty (Lisp_Object process) | |||
| 2301 | we just reopen the device (see emacs_get_tty_pgrp) as this is | 2332 | we just reopen the device (see emacs_get_tty_pgrp) as this is |
| 2302 | more portable (see USG_SUBTTY_WORKS above). */ | 2333 | more portable (see USG_SUBTTY_WORKS above). */ |
| 2303 | 2334 | ||
| 2304 | p->pty_flag = 1; | 2335 | p->pty_in = p->pty_out = true; |
| 2305 | pset_status (p, Qrun); | 2336 | pset_status (p, Qrun); |
| 2306 | setup_process_coding_systems (process); | 2337 | setup_process_coding_systems (process); |
| 2307 | 2338 | ||
| @@ -2412,7 +2443,7 @@ usage: (make-pipe-process &rest ARGS) */) | |||
| 2412 | p->kill_without_query = 1; | 2443 | p->kill_without_query = 1; |
| 2413 | if (tem = plist_get (contact, QCstop), !NILP (tem)) | 2444 | if (tem = plist_get (contact, QCstop), !NILP (tem)) |
| 2414 | pset_command (p, Qt); | 2445 | pset_command (p, Qt); |
| 2415 | eassert (! p->pty_flag); | 2446 | eassert (! p->pty_in && ! p->pty_out); |
| 2416 | 2447 | ||
| 2417 | if (!EQ (p->command, Qt) | 2448 | if (!EQ (p->command, Qt) |
| 2418 | && !EQ (p->filter, Qt)) | 2449 | && !EQ (p->filter, Qt)) |
| @@ -3147,7 +3178,7 @@ usage: (make-serial-process &rest ARGS) */) | |||
| 3147 | p->kill_without_query = 1; | 3178 | p->kill_without_query = 1; |
| 3148 | if (tem = plist_get (contact, QCstop), !NILP (tem)) | 3179 | if (tem = plist_get (contact, QCstop), !NILP (tem)) |
| 3149 | pset_command (p, Qt); | 3180 | pset_command (p, Qt); |
| 3150 | eassert (! p->pty_flag); | 3181 | eassert (! p->pty_in && ! p->pty_out); |
| 3151 | 3182 | ||
| 3152 | if (!EQ (p->command, Qt) | 3183 | if (!EQ (p->command, Qt) |
| 3153 | && !EQ (p->filter, Qt)) | 3184 | && !EQ (p->filter, Qt)) |
| @@ -6808,7 +6839,7 @@ process_send_signal (Lisp_Object process, int signo, Lisp_Object current_group, | |||
| 6808 | error ("Process %s is not active", | 6839 | error ("Process %s is not active", |
| 6809 | SDATA (p->name)); | 6840 | SDATA (p->name)); |
| 6810 | 6841 | ||
| 6811 | if (!p->pty_flag) | 6842 | if (! p->pty_in) |
| 6812 | current_group = Qnil; | 6843 | current_group = Qnil; |
| 6813 | 6844 | ||
| 6814 | /* If we are using pgrps, get a pgrp number and make it negative. */ | 6845 | /* If we are using pgrps, get a pgrp number and make it negative. */ |
| @@ -7177,7 +7208,7 @@ process has been transmitted to the serial port. */) | |||
| 7177 | send_process (proc, "", 0, Qnil); | 7208 | send_process (proc, "", 0, Qnil); |
| 7178 | } | 7209 | } |
| 7179 | 7210 | ||
| 7180 | if (XPROCESS (proc)->pty_flag) | 7211 | if (XPROCESS (proc)->pty_in) |
| 7181 | send_process (proc, "\004", 1, Qnil); | 7212 | send_process (proc, "\004", 1, Qnil); |
| 7182 | else if (EQ (XPROCESS (proc)->type, Qserial)) | 7213 | else if (EQ (XPROCESS (proc)->type, Qserial)) |
| 7183 | { | 7214 | { |
diff --git a/src/process.h b/src/process.h index 392b661ce69..92baf0c4cb9 100644 --- a/src/process.h +++ b/src/process.h | |||
| @@ -156,8 +156,9 @@ struct Lisp_Process | |||
| 156 | /* True means kill silently if Emacs is exited. | 156 | /* True means kill silently if Emacs is exited. |
| 157 | This is the inverse of the `query-on-exit' flag. */ | 157 | This is the inverse of the `query-on-exit' flag. */ |
| 158 | bool_bf kill_without_query : 1; | 158 | bool_bf kill_without_query : 1; |
| 159 | /* True if communicating through a pty. */ | 159 | /* True if communicating through a pty for input or output. */ |
| 160 | bool_bf pty_flag : 1; | 160 | bool_bf pty_in : 1; |
| 161 | bool_bf pty_out : 1; | ||
| 161 | /* Flag to set coding-system of the process buffer from the | 162 | /* Flag to set coding-system of the process buffer from the |
| 162 | coding_system used to decode process output. */ | 163 | coding_system used to decode process output. */ |
| 163 | bool_bf inherit_coding_system_flag : 1; | 164 | bool_bf inherit_coding_system_flag : 1; |
diff --git a/test/lisp/eshell/esh-proc-tests.el b/test/lisp/eshell/esh-proc-tests.el index 7f461d1813c..734bb91a6a5 100644 --- a/test/lisp/eshell/esh-proc-tests.el +++ b/test/lisp/eshell/esh-proc-tests.el | |||
| @@ -28,6 +28,15 @@ | |||
| 28 | (file-name-directory (or load-file-name | 28 | (file-name-directory (or load-file-name |
| 29 | default-directory)))) | 29 | default-directory)))) |
| 30 | 30 | ||
| 31 | (defvar esh-proc-test--detect-pty-cmd | ||
| 32 | (concat "sh -c '" | ||
| 33 | "if [ -t 0 ]; then echo stdin; fi; " | ||
| 34 | "if [ -t 1 ]; then echo stdout; fi; " | ||
| 35 | "if [ -t 2 ]; then echo stderr; fi" | ||
| 36 | "'")) | ||
| 37 | |||
| 38 | ;;; Tests: | ||
| 39 | |||
| 31 | (ert-deftest esh-proc-test/sigpipe-exits-process () | 40 | (ert-deftest esh-proc-test/sigpipe-exits-process () |
| 32 | "Test that a SIGPIPE is properly sent to a process if a pipe closes" | 41 | "Test that a SIGPIPE is properly sent to a process if a pipe closes" |
| 33 | (skip-unless (and (executable-find "sh") | 42 | (skip-unless (and (executable-find "sh") |
| @@ -44,6 +53,40 @@ | |||
| 44 | (eshell-wait-for-subprocess t) | 53 | (eshell-wait-for-subprocess t) |
| 45 | (should (eq (process-list) nil)))) | 54 | (should (eq (process-list) nil)))) |
| 46 | 55 | ||
| 56 | (ert-deftest esh-proc-test/pipeline-connection-type/no-pipeline () | ||
| 57 | "Test that all streams are PTYs when a command is not in a pipeline." | ||
| 58 | (skip-unless (executable-find "sh")) | ||
| 59 | (should (equal (eshell-test-command-result esh-proc-test--detect-pty-cmd) | ||
| 60 | ;; PTYs aren't supported on MS-Windows. | ||
| 61 | (unless (eq system-type 'windows-nt) | ||
| 62 | "stdin\nstdout\nstderr\n")))) | ||
| 63 | |||
| 64 | (ert-deftest esh-proc-test/pipeline-connection-type/first () | ||
| 65 | "Test that only stdin is a PTY when a command starts a pipeline." | ||
| 66 | (skip-unless (and (executable-find "sh") | ||
| 67 | (executable-find "cat"))) | ||
| 68 | (should (equal (eshell-test-command-result | ||
| 69 | (concat esh-proc-test--detect-pty-cmd " | cat")) | ||
| 70 | (unless (eq system-type 'windows-nt) | ||
| 71 | "stdin\n")))) | ||
| 72 | |||
| 73 | (ert-deftest esh-proc-test/pipeline-connection-type/middle () | ||
| 74 | "Test that all streams are pipes when a command is in the middle of a | ||
| 75 | pipeline." | ||
| 76 | (skip-unless (and (executable-find "sh") | ||
| 77 | (executable-find "cat"))) | ||
| 78 | (should (equal (eshell-test-command-result | ||
| 79 | (concat "echo | " esh-proc-test--detect-pty-cmd " | cat")) | ||
| 80 | nil))) | ||
| 81 | |||
| 82 | (ert-deftest esh-proc-test/pipeline-connection-type/last () | ||
| 83 | "Test that only output streams are PTYs when a command ends a pipeline." | ||
| 84 | (skip-unless (executable-find "sh")) | ||
| 85 | (should (equal (eshell-test-command-result | ||
| 86 | (concat "echo | " esh-proc-test--detect-pty-cmd)) | ||
| 87 | (unless (eq system-type 'windows-nt) | ||
| 88 | "stdout\nstderr\n")))) | ||
| 89 | |||
| 47 | (ert-deftest esh-proc-test/kill-pipeline () | 90 | (ert-deftest esh-proc-test/kill-pipeline () |
| 48 | "Test that killing a pipeline of processes only emits a single | 91 | "Test that killing a pipeline of processes only emits a single |
| 49 | prompt. See bug#54136." | 92 | prompt. See bug#54136." |
diff --git a/test/src/process-tests.el b/test/src/process-tests.el index aab95b2d733..b801563feb7 100644 --- a/test/src/process-tests.el +++ b/test/src/process-tests.el | |||
| @@ -38,10 +38,11 @@ | |||
| 38 | ;; Timeout in seconds; the test fails if the timeout is reached. | 38 | ;; Timeout in seconds; the test fails if the timeout is reached. |
| 39 | (defvar process-test-sentinel-wait-timeout 2.0) | 39 | (defvar process-test-sentinel-wait-timeout 2.0) |
| 40 | 40 | ||
| 41 | ;; Start a process that exits immediately. Call WAIT-FUNCTION, | 41 | (defun process-test-wait-for-sentinel (proc exit-status &optional wait-function) |
| 42 | ;; possibly multiple times, to wait for the process to complete. | 42 | "Set a sentinel on PROC and wait for it to be called with EXIT-STATUS. |
| 43 | (defun process-test-sentinel-wait-function-working-p (wait-function) | 43 | Call WAIT-FUNCTION, possibly multiple times, to wait for the |
| 44 | (let ((proc (start-process "test" nil "bash" "-c" "exit 20")) | 44 | process to complete." |
| 45 | (let ((wait-function (or wait-function #'accept-process-output)) | ||
| 45 | (sentinel-called nil) | 46 | (sentinel-called nil) |
| 46 | (start-time (float-time))) | 47 | (start-time (float-time))) |
| 47 | (set-process-sentinel proc (lambda (_proc _msg) | 48 | (set-process-sentinel proc (lambda (_proc _msg) |
| @@ -50,21 +51,22 @@ | |||
| 50 | (> (- (float-time) start-time) | 51 | (> (- (float-time) start-time) |
| 51 | process-test-sentinel-wait-timeout))) | 52 | process-test-sentinel-wait-timeout))) |
| 52 | (funcall wait-function)) | 53 | (funcall wait-function)) |
| 53 | (cl-assert (eq (process-status proc) 'exit)) | 54 | (should sentinel-called) |
| 54 | (cl-assert (= (process-exit-status proc) 20)) | 55 | (should (eq (process-status proc) 'exit)) |
| 55 | sentinel-called)) | 56 | (should (= (process-exit-status proc) exit-status)))) |
| 56 | 57 | ||
| 57 | (ert-deftest process-test-sentinel-accept-process-output () | 58 | (ert-deftest process-test-sentinel-accept-process-output () |
| 58 | (skip-unless (executable-find "bash")) | 59 | (skip-unless (executable-find "bash")) |
| 59 | (with-timeout (60 (ert-fail "Test timed out")) | 60 | (with-timeout (60 (ert-fail "Test timed out")) |
| 60 | (should (process-test-sentinel-wait-function-working-p | 61 | (let ((proc (start-process "test" nil "bash" "-c" "exit 20"))) |
| 61 | #'accept-process-output)))) | 62 | (should (process-test-wait-for-sentinel proc 20))))) |
| 62 | 63 | ||
| 63 | (ert-deftest process-test-sentinel-sit-for () | 64 | (ert-deftest process-test-sentinel-sit-for () |
| 64 | (skip-unless (executable-find "bash")) | 65 | (skip-unless (executable-find "bash")) |
| 65 | (with-timeout (60 (ert-fail "Test timed out")) | 66 | (with-timeout (60 (ert-fail "Test timed out")) |
| 66 | (should | 67 | (let ((proc (start-process "test" nil "bash" "-c" "exit 20"))) |
| 67 | (process-test-sentinel-wait-function-working-p (lambda () (sit-for 0.01 t)))))) | 68 | (should (process-test-wait-for-sentinel |
| 69 | proc 20 (lambda () (sit-for 0.01 t))))))) | ||
| 68 | 70 | ||
| 69 | (when (eq system-type 'windows-nt) | 71 | (when (eq system-type 'windows-nt) |
| 70 | (ert-deftest process-test-quoted-batfile () | 72 | (ert-deftest process-test-quoted-batfile () |
| @@ -97,17 +99,8 @@ | |||
| 97 | "echo hello stderr! >&2; " | 99 | "echo hello stderr! >&2; " |
| 98 | "exit 20")) | 100 | "exit 20")) |
| 99 | :buffer stdout-buffer | 101 | :buffer stdout-buffer |
| 100 | :stderr stderr-buffer)) | 102 | :stderr stderr-buffer))) |
| 101 | (sentinel-called nil) | 103 | (process-test-wait-for-sentinel proc 20) |
| 102 | (start-time (float-time))) | ||
| 103 | (set-process-sentinel proc (lambda (_proc _msg) | ||
| 104 | (setq sentinel-called t))) | ||
| 105 | (while (not (or sentinel-called | ||
| 106 | (> (- (float-time) start-time) | ||
| 107 | process-test-sentinel-wait-timeout))) | ||
| 108 | (accept-process-output)) | ||
| 109 | (cl-assert (eq (process-status proc) 'exit)) | ||
| 110 | (cl-assert (= (process-exit-status proc) 20)) | ||
| 111 | (should (with-current-buffer stdout-buffer | 104 | (should (with-current-buffer stdout-buffer |
| 112 | (goto-char (point-min)) | 105 | (goto-char (point-min)) |
| 113 | (looking-at "hello stdout!"))) | 106 | (looking-at "hello stdout!"))) |
| @@ -118,8 +111,7 @@ | |||
| 118 | (ert-deftest process-test-stderr-filter () | 111 | (ert-deftest process-test-stderr-filter () |
| 119 | (skip-unless (executable-find "bash")) | 112 | (skip-unless (executable-find "bash")) |
| 120 | (with-timeout (60 (ert-fail "Test timed out")) | 113 | (with-timeout (60 (ert-fail "Test timed out")) |
| 121 | (let* ((sentinel-called nil) | 114 | (let* ((stderr-sentinel-called nil) |
| 122 | (stderr-sentinel-called nil) | ||
| 123 | (stdout-output nil) | 115 | (stdout-output nil) |
| 124 | (stderr-output nil) | 116 | (stderr-output nil) |
| 125 | (stdout-buffer (generate-new-buffer "*stdout*")) | 117 | (stdout-buffer (generate-new-buffer "*stdout*")) |
| @@ -131,23 +123,14 @@ | |||
| 131 | (concat "echo hello stdout!; " | 123 | (concat "echo hello stdout!; " |
| 132 | "echo hello stderr! >&2; " | 124 | "echo hello stderr! >&2; " |
| 133 | "exit 20")) | 125 | "exit 20")) |
| 134 | :stderr stderr-proc)) | 126 | :stderr stderr-proc))) |
| 135 | (start-time (float-time))) | ||
| 136 | (set-process-filter proc (lambda (_proc input) | 127 | (set-process-filter proc (lambda (_proc input) |
| 137 | (push input stdout-output))) | 128 | (push input stdout-output))) |
| 138 | (set-process-sentinel proc (lambda (_proc _msg) | ||
| 139 | (setq sentinel-called t))) | ||
| 140 | (set-process-filter stderr-proc (lambda (_proc input) | 129 | (set-process-filter stderr-proc (lambda (_proc input) |
| 141 | (push input stderr-output))) | 130 | (push input stderr-output))) |
| 142 | (set-process-sentinel stderr-proc (lambda (_proc _input) | 131 | (set-process-sentinel stderr-proc (lambda (_proc _input) |
| 143 | (setq stderr-sentinel-called t))) | 132 | (setq stderr-sentinel-called t))) |
| 144 | (while (not (or sentinel-called | 133 | (process-test-wait-for-sentinel proc 20) |
| 145 | (> (- (float-time) start-time) | ||
| 146 | process-test-sentinel-wait-timeout))) | ||
| 147 | (accept-process-output)) | ||
| 148 | (cl-assert (eq (process-status proc) 'exit)) | ||
| 149 | (cl-assert (= (process-exit-status proc) 20)) | ||
| 150 | (should sentinel-called) | ||
| 151 | (should (equal 1 (with-current-buffer stdout-buffer | 134 | (should (equal 1 (with-current-buffer stdout-buffer |
| 152 | (point-max)))) | 135 | (point-max)))) |
| 153 | (should (equal "hello stdout!\n" | 136 | (should (equal "hello stdout!\n" |
| @@ -289,6 +272,74 @@ | |||
| 289 | (error :got-error)))) | 272 | (error :got-error)))) |
| 290 | (should have-called-debugger)))) | 273 | (should have-called-debugger)))) |
| 291 | 274 | ||
| 275 | (defun make-process/test-connection-type (ttys &rest args) | ||
| 276 | "Make a process and check whether its standard streams match TTYS. | ||
| 277 | This calls `make-process', passing ARGS to adjust how the process | ||
| 278 | is created. TTYS should be a list of 3 boolean values, | ||
| 279 | indicating whether the subprocess's stdin, stdout, and stderr | ||
| 280 | should be a TTY, respectively." | ||
| 281 | (declare (indent 1)) | ||
| 282 | (let* (;; MS-Windows doesn't support communicating via pty. | ||
| 283 | (ttys (if (eq system-type 'windows-nt) '(nil nil nil) ttys)) | ||
| 284 | (expected-output (concat (and (nth 0 ttys) "stdin\n") | ||
| 285 | (and (nth 1 ttys) "stdout\n") | ||
| 286 | (and (nth 2 ttys) "stderr\n"))) | ||
| 287 | (stdout-buffer (generate-new-buffer "*stdout*")) | ||
| 288 | (proc (apply | ||
| 289 | #'make-process | ||
| 290 | :name "test" | ||
| 291 | :command (list "sh" "-c" | ||
| 292 | (concat "if [ -t 0 ]; then echo stdin; fi; " | ||
| 293 | "if [ -t 1 ]; then echo stdout; fi; " | ||
| 294 | "if [ -t 2 ]; then echo stderr; fi")) | ||
| 295 | :buffer stdout-buffer | ||
| 296 | args))) | ||
| 297 | (process-test-wait-for-sentinel proc 0) | ||
| 298 | (should (equal (with-current-buffer stdout-buffer (buffer-string)) | ||
| 299 | expected-output)))) | ||
| 300 | |||
| 301 | (ert-deftest make-process/connection-type/pty () | ||
| 302 | (skip-unless (executable-find "sh")) | ||
| 303 | (make-process/test-connection-type '(t t t) | ||
| 304 | :connection-type 'pty)) | ||
| 305 | |||
| 306 | (ert-deftest make-process/connection-type/pty-2 () | ||
| 307 | (skip-unless (executable-find "sh")) | ||
| 308 | (make-process/test-connection-type '(t t t) | ||
| 309 | :connection-type '(pty . pty))) | ||
| 310 | |||
| 311 | (ert-deftest make-process/connection-type/pipe () | ||
| 312 | (skip-unless (executable-find "sh")) | ||
| 313 | (make-process/test-connection-type '(nil nil nil) | ||
| 314 | :connection-type 'pipe)) | ||
| 315 | |||
| 316 | (ert-deftest make-process/connection-type/pipe-2 () | ||
| 317 | (skip-unless (executable-find "sh")) | ||
| 318 | (make-process/test-connection-type '(nil nil nil) | ||
| 319 | :connection-type '(pipe . pipe))) | ||
| 320 | |||
| 321 | (ert-deftest make-process/connection-type/in-pty () | ||
| 322 | (skip-unless (executable-find "sh")) | ||
| 323 | (make-process/test-connection-type '(t nil nil) | ||
| 324 | :connection-type '(pty . pipe))) | ||
| 325 | |||
| 326 | (ert-deftest make-process/connection-type/out-pty () | ||
| 327 | (skip-unless (executable-find "sh")) | ||
| 328 | (make-process/test-connection-type '(nil t t) | ||
| 329 | :connection-type '(pipe . pty))) | ||
| 330 | |||
| 331 | (ert-deftest make-process/connection-type/pty-with-stderr-buffer () | ||
| 332 | (skip-unless (executable-find "sh")) | ||
| 333 | (let ((stderr-buffer (generate-new-buffer "*stderr*"))) | ||
| 334 | (make-process/test-connection-type '(t t nil) | ||
| 335 | :connection-type 'pty :stderr stderr-buffer))) | ||
| 336 | |||
| 337 | (ert-deftest make-process/connection-type/out-pty-with-stderr-buffer () | ||
| 338 | (skip-unless (executable-find "sh")) | ||
| 339 | (let ((stderr-buffer (generate-new-buffer "*stderr*"))) | ||
| 340 | (make-process/test-connection-type '(nil t nil) | ||
| 341 | :connection-type '(pipe . pty) :stderr stderr-buffer))) | ||
| 342 | |||
| 292 | (ert-deftest make-process/file-handler/found () | 343 | (ert-deftest make-process/file-handler/found () |
| 293 | "Check that the `:file-handler’ argument of `make-process’ | 344 | "Check that the `:file-handler’ argument of `make-process’ |
| 294 | works as expected if a file name handler is found." | 345 | works as expected if a file name handler is found." |