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 /src/process.c | |
| 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).
Diffstat (limited to 'src/process.c')
| -rw-r--r-- | src/process.c | 129 |
1 files changed, 80 insertions, 49 deletions
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 | { |