1
2#' @useDynLib processx, .registration = TRUE, .fixes = "c_"
3NULL
4
5## Workaround an R CMD check false positive
6dummy_r6 <- function() R6::R6Class
7
8#' External process
9#'
10#' @description
11#' Managing external processes from R is not trivial, and this
12#' class aims to help with this deficiency. It is essentially a small
13#' wrapper around the `system` base R function, to return the process
14#' id of the started process, and set its standard output and error
15#' streams. The process id is then used to manage the process.
16#'
17#' @param n Number of characters or lines to read.
18#' @param grace Currently not used.
19#' @param close_connections Whether to close standard input, standard
20#'   output, standard error connections and the poll connection, after
21#'   killing the process.
22#' @param timeout Timeout in milliseconds, for the wait or the I/O
23#'   polling.
24#'
25#' @section Batch files:
26#' Running Windows batch files (`.bat` or `.cmd` files) may be complicated
27#' because of the `cmd.exe` command line parsing rules. For example you
28#' cannot easily have whitespace in both the command (path) and one of the
29#' arguments. To work around these limitations you need to start a
30#' `cmd.exe` shell explicitly and use its `call` command. For example:
31#'
32#' ```r
33#' process$new("cmd.exe", c("/c", "call", bat_file, "arg 1", "arg 2"))
34#' ```
35#'
36#' This works even if `bat_file` contains whitespace characters.
37#'
38#' @section Polling:
39#' The `poll_io()` function polls the standard output and standard
40#' error connections of a process, with a timeout. If there is output
41#' in either of them, or they are closed (e.g. because the process exits)
42#' `poll_io()` returns immediately.
43#'
44#' In addition to polling a single process, the [poll()] function
45#' can poll the output of several processes, and returns as soon as any
46#' of them has generated output (or exited).
47#'
48#' @section Cleaning up background processes:
49#' processx kills processes that are not referenced any more (if `cleanup`
50#' is set to `TRUE`), or the whole subprocess tree (if `cleanup_tree` is
51#' also set to `TRUE`).
52#'
53#' The cleanup happens when the references of the processes object are
54#' garbage collected. To clean up earlier, you can call the `kill()` or
55#' `kill_tree()` method of the process(es), from an `on.exit()` expression,
56#' or an error handler:
57#' ```r
58#' process_manager <- function() {
59#'   on.exit({
60#'     try(p1$kill(), silent = TRUE)
61#'     try(p2$kill(), silent = TRUE)
62#'   }, add = TRUE)
63#'   p1 <- process$new("sleep", "3")
64#'   p2 <- process$new("sleep", "10")
65#'   p1$wait()
66#'   p2$wait()
67#' }
68#' process_manager()
69#' ```
70#'
71#' If you interrupt `process_manager()` or an error happens then both `p1`
72#' and `p2` are cleaned up immediately. Their connections will also be
73#' closed. The same happens at a regular exit.
74#'
75#' @export
76#' @examplesIf identical(Sys.getenv("IN_PKGDOWN"), "true")
77#' p <- process$new("sleep", "2")
78#' p$is_alive()
79#' p
80#' p$kill()
81#' p$is_alive()
82#'
83#' p <- process$new("sleep", "1")
84#' p$is_alive()
85#' Sys.sleep(2)
86#' p$is_alive()
87
88process <- R6::R6Class(
89  "process",
90  cloneable = FALSE,
91  public = list(
92
93    #' @description
94    #' Start a new process in the background, and then return immediately.
95    #'
96    #' @return R6 object representing the process.
97    #' @param command Character scalar, the command to run.
98    #'   Note that this argument is not passed to a shell, so no
99    #'   tilde-expansion or variable substitution is performed on it.
100    #'   It should not be quoted with [base::shQuote()]. See
101    #'   [base::normalizePath()] for tilde-expansion. If you want to run
102    #'   `.bat` or `.cmd` files on Windows, make sure you read the
103    #'   'Batch files' section above.
104    #' @param args Character vector, arguments to the command. They will be
105    #'   passed to the process as is, without a shell transforming them,
106    #'   They don't need to be escaped.
107    #' @param stdin What to do with the standard input. Possible values:
108    #'   * `NULL`: set to the _null device_, i.e. no standard input is
109    #'     provided;
110    #'   * a file name, use this file as standard input;
111    #'   * `"|"`: create a (writeable) connection for stdin.
112    #'   * `""` (empty string): inherit it from the main R process. If the
113    #'     main R process does not have a standard input stream, e.g. in
114    #'     RGui on Windows, then an error is thrown.
115    #' @param stdout  What to do with the standard output. Possible values:
116    #'   * `NULL`: discard it;
117    #'   * a string, redirect it to this file;
118    #'   * `"|"`: create a connection for it.
119    #'   * `""` (empty string): inherit it from the main R process. If the
120    #'     main R process does not have a standard output stream, e.g. in
121    #'     RGui on Windows, then an error is thrown.
122    #' @param stderr What to do with the standard error. Possible values:
123    #'   * `NULL`: discard it;
124    #'   * a string, redirect it to this file;
125    #'   * `"|"`: create a connection for it;
126    #'   * `"2>&1"`: redirect it to the same connection (i.e. pipe or file)
127    #'     as `stdout`. `"2>&1"` is a way to keep standard output and error
128    #'     correctly interleaved.
129    #'   * `""` (empty string): inherit it from the main R process. If the
130    #'     main R process does not have a standard error stream, e.g. in
131    #'     RGui on Windows, then an error is thrown.
132    #' @param pty Whether to create a pseudo terminal (pty) for the
133    #'   background process. This is currently only supported on Unix
134    #'   systems, but not supported on Solaris.
135    #'   If it is `TRUE`, then the `stdin`, `stdout` and `stderr` arguments
136    #'   must be `NULL`. If a pseudo terminal is created, then processx
137    #'   will create pipes for standard input and standard output. There is
138    #'   no separate pipe for standard error, because there is no way to
139    #'   distinguish between stdout and stderr on a pty. Note that the
140    #'   standard output connection of the pty is _blocking_, so we always
141    #'   poll the standard output connection before reading from it using
142    #'   the `$read_output()` method. Also, because `$read_output_lines()`
143    #'   could still block if no complete line is available, this function
144    #'   always fails if the process has a pty. Use `$read_output()` to
145    #'   read from ptys.
146    #' @param pty_options Unix pseudo terminal options, a named list. see
147    #'   [default_pty_options()] for details and defaults.
148    #' @param connections A list of processx connections to pass to the
149    #'   child process. This is an experimental feature currently.
150    #' @param poll_connection Whether to create an extra connection to the
151    #'   process that allows polling, even if the standard input and
152    #'   standard output are not pipes. If this is `NULL` (the default),
153    #'   then this connection will be only created if standard output and
154    #'   standard error are not pipes, and `connections` is an empty list.
155    #'   If the poll connection is created, you can query it via
156    #'   `p$get_poll_connection()` and it is also included in the response
157    #'   to `p$poll_io()` and [poll()]. The numeric file descriptor of the
158    #'   poll connection comes right after `stderr` (2), and the
159    #'   connections listed in `connections`.
160    #' @param env Environment variables of the child process. If `NULL`,
161    #'   the parent's environment is inherited. On Windows, many programs
162    #'   cannot function correctly if some environment variables are not
163    #'   set, so we always set `HOMEDRIVE`, `HOMEPATH`, `LOGONSERVER`,
164    #'   `PATH`, `SYSTEMDRIVE`, `SYSTEMROOT`, `TEMP`, `USERDOMAIN`,
165    #'   `USERNAME`, `USERPROFILE` and `WINDIR`. To append new environment
166    #'   variables to the ones set in the current process, specify
167    #'   `"current"` in `env`, without a name, and the appended ones with
168    #'   names. The appended ones can overwrite the current ones.
169    #' @param cleanup Whether to kill the process when the `process`
170    #'   object is garbage collected.
171    #' @param cleanup_tree Whether to kill the process and its child
172    #'   process tree when the `process` object is garbage collected.
173    #' @param wd Working directory of the process. It must exist.
174    #'   If `NULL`, then the current working directory is used.
175    #' @param echo_cmd Whether to print the command to the screen before
176    #'   running it.
177    #' @param supervise Whether to register the process with a supervisor.
178    #'   If `TRUE`, the supervisor will ensure that the process is
179    #'   killed when the R process exits.
180    #' @param windows_verbatim_args Whether to omit quoting the arguments
181    #'   on Windows. It is ignored on other platforms.
182    #' @param windows_hide_window Whether to hide the application's window
183    #'   on Windows. It is ignored on other platforms.
184    #' @param windows_detached_process Whether to use the
185    #'   `DETACHED_PROCESS` flag on Windows. If this is `TRUE`, then
186    #'   the child process will have no attached console, even if the
187    #'   parent had one.
188    #' @param encoding The encoding to assume for `stdin`, `stdout` and
189    #'   `stderr`. By default the encoding of the current locale is
190    #'   used. Note that `processx` always reencodes the output of the
191    #'  `stdout` and `stderr` streams in UTF-8 currently.
192    #'   If you want to read them without any conversion, on all platforms,
193    #'   specify `"UTF-8"` as encoding.
194    #' @param post_process An optional function to run when the process has
195    #'   finished. Currently it only runs if `$get_result()` is called.
196    #'   It is only run once.
197
198    initialize = function(command = NULL, args = character(),
199      stdin = NULL, stdout = NULL, stderr = NULL, pty = FALSE,
200      pty_options = list(), connections = list(), poll_connection = NULL,
201      env = NULL, cleanup = TRUE, cleanup_tree = FALSE, wd = NULL,
202      echo_cmd = FALSE, supervise = FALSE, windows_verbatim_args = FALSE,
203      windows_hide_window = FALSE, windows_detached_process = !cleanup,
204      encoding = "",  post_process = NULL)
205
206      process_initialize(self, private, command, args, stdin,
207                         stdout, stderr, pty, pty_options, connections,
208                         poll_connection, env, cleanup, cleanup_tree, wd,
209                         echo_cmd, supervise, windows_verbatim_args,
210                         windows_hide_window, windows_detached_process,
211                         encoding, post_process),
212
213    #' @description
214    #' Cleanup method that is called when the `process` object is garbage
215    #' collected. If requested so in the process constructor, then it
216    #' eliminates all processes in the process's subprocess tree.
217
218    finalize = function() {
219      if (!is.null(private$tree_id) && private$cleanup_tree &&
220          ps::ps_is_supported()) self$kill_tree()
221    },
222
223    #' @description
224    #' Terminate the process. It also terminate all of its child
225    #' processes, except if they have created a new process group (on Unix),
226    #' or job object (on Windows). It returns `TRUE` if the process
227    #' was terminated, and `FALSE` if it was not (because it was
228    #' already finished/dead when `processx` tried to terminate it).
229
230    kill = function(grace = 0.1, close_connections = TRUE)
231      process_kill(self, private, grace, close_connections),
232
233    #' @description
234    #' Process tree cleanup. It terminates the process
235    #' (if still alive), together with any child (or grandchild, etc.)
236    #' processes. It uses the _ps_ package, so that needs to be installed,
237    #' and _ps_ needs to support the current platform as well. Process tree
238    #' cleanup works by marking the process with an environment variable,
239    #' which is inherited in all child processes. This allows finding
240    #' descendents, even if they are orphaned, i.e. they are not connected
241    #' to the root of the tree cleanup in the process tree any more.
242    #' `$kill_tree()` returns a named integer vector of the process ids that
243    #' were killed, the names are the names of the processes (e.g. `"sleep"`,
244    #' `"notepad.exe"`, `"Rterm.exe"`, etc.).
245
246    kill_tree = function(grace = 0.1, close_connections = TRUE)
247      process_kill_tree(self, private, grace, close_connections),
248
249    #' @description
250    #' Send a signal to the process. On Windows only the
251    #' `SIGINT`, `SIGTERM` and `SIGKILL` signals are interpreted,
252    #' and the special 0 signal. The first three all kill the process. The 0
253    #' signal returns `TRUE` if the process is alive, and `FALSE`
254    #' otherwise. On Unix all signals are supported that the OS supports,
255    #' and the 0 signal as well.
256    #' @param signal An integer scalar, the id of the signal to send to
257    #'   the process. See [tools::pskill()] for the list of signals.
258
259    signal = function(signal)
260      process_signal(self, private, signal),
261
262    #' @description
263    #' Send an interrupt to the process. On Unix this is a
264    #' `SIGINT` signal, and it is usually equivalent to pressing CTRL+C at
265    #' the terminal prompt. On Windows, it is a CTRL+BREAK keypress.
266    #' Applications may catch these events. By default they will quit.
267
268    interrupt = function()
269      process_interrupt(self, private),
270
271    #' @description
272    #' Query the process id.
273    #' @return Integer scalar, the process id of the process.
274
275    get_pid = function()
276      process_get_pid(self, private),
277
278    #' @description Check if the process is alive.
279    #' @return Logical scalar.
280
281    is_alive = function()
282      process_is_alive(self, private),
283
284    #' @description
285    #' Wait until the process finishes, or a timeout happens.
286    #' Note that if the process never finishes, and the timeout is infinite
287    #' (the default), then R will never regain control. In some rare cases,
288    #' `$wait()` might take a bit longer than specified to time out. This
289    #' happens on Unix, when another package overwrites the processx
290    #' `SIGCHLD` signal handler, after the processx process has started.
291    #' One such package is parallel, if used with fork clusters, e.g.
292    #' through `parallel::mcparallel()`.
293    #' @return It returns the process itself, invisibly.
294
295    wait = function(timeout = -1)
296      process_wait(self, private, timeout),
297
298    #' @description
299    #' `$get_exit_status` returns the exit code of the process if it has
300    #' finished and `NULL` otherwise. On Unix, in some rare cases, the exit
301    #' status might be `NA`. This happens if another package (or R itself)
302    #' overwrites the processx `SIGCHLD` handler, after the processx process
303    #' has started. In these cases processx cannot determine the real exit
304    #' status of the process. One such package is parallel, if used with
305    #' fork clusters, e.g. through the `parallel::mcparallel()` function.
306
307    get_exit_status = function()
308      process_get_exit_status(self, private),
309
310    #' @description
311    #' `format(p)` or `p$format()` creates a string representation of the
312    #' process, usually for printing.
313
314    format = function()
315      process_format(self, private),
316
317    #' @description
318    #' `print(p)` or `p$print()` shows some information about the
319    #' process on the screen, whether it is running and it's process id, etc.
320
321    print = function()
322      process_print(self, private),
323
324    #' @description
325    #' `$get_start_time()` returns the time when the process was
326    #' started.
327
328    get_start_time = function()
329      process_get_start_time(self, private),
330
331    #' @description
332    #' `$is_supervised()` returns whether the process is being tracked by
333    #' supervisor process.
334
335    is_supervised = function()
336      process_is_supervised(self, private),
337
338    #' @description
339    #' `$supervise()` if passed `TRUE`, tells the supervisor to start
340    #' tracking the process. If `FALSE`, tells the supervisor to stop
341    #' tracking the process. Note that even if the supervisor is disabled
342    #' for a process, if it was started with `cleanup = TRUE`, the process
343    #' will still be killed when the object is garbage collected.
344    #' @param status Whether to turn on of off the supervisor for this
345    #'   process.
346
347    supervise = function(status)
348      process_supervise(self, private, status),
349
350    ## Output
351
352    #' @description
353    #' `$read_output()` reads from the standard output connection of the
354    #' process. If the standard output connection was not requested, then
355    #' then it returns an error. It uses a non-blocking text connection. This
356    #' will work only if `stdout="|"` was used. Otherwise, it will throw an
357    #' error.
358
359    read_output = function(n = -1)
360      process_read_output(self, private, n),
361
362    #' @description
363    #' `$read_error()` is similar to `$read_output`, but it reads
364    #' from the standard error stream.
365
366    read_error = function(n = -1)
367      process_read_error(self, private, n),
368
369    #' @description
370    #' `$read_output_lines()` reads lines from standard output connection
371    #' of the process. If the standard output connection was not requested,
372    #' then it returns an error. It uses a non-blocking text connection.
373    #' This will work only if `stdout="|"` was used. Otherwise, it will
374    #' throw an error.
375
376    read_output_lines = function(n = -1)
377      process_read_output_lines(self, private, n),
378
379    #' @description
380    #' `$read_error_lines()` is similar to `$read_output_lines`, but
381    #' it reads from the standard error stream.
382
383    read_error_lines = function(n = -1)
384      process_read_error_lines(self, private, n),
385
386    #' @description
387    #' `$is_incomplete_output()` return `FALSE` if the other end of
388    #' the standard output connection was closed (most probably because the
389    #' process exited). It return `TRUE` otherwise.
390
391    is_incomplete_output = function()
392      process_is_incompelete_output(self, private),
393
394    #' @description
395    #' `$is_incomplete_error()` return `FALSE` if the other end of
396    #' the standard error connection was closed (most probably because the
397    #' process exited). It return `TRUE` otherwise.
398
399    is_incomplete_error = function()
400      process_is_incompelete_error(self, private),
401
402    #' @description
403    #' `$has_input_connection()` return `TRUE` if there is a connection
404    #' object for standard input; in other words, if `stdout="|"`. It returns
405    #' `FALSE` otherwise.
406
407    has_input_connection = function()
408      process_has_input_connection(self, private),
409
410    #' @description
411    #' `$has_output_connection()` returns `TRUE` if there is a connection
412    #' object for standard output; in other words, if `stdout="|"`. It returns
413    #' `FALSE` otherwise.
414
415    has_output_connection = function()
416      process_has_output_connection(self, private),
417
418    #' @description
419    #' `$has_error_connection()` returns `TRUE` if there is a connection
420    #' object for standard error; in other words, if `stderr="|"`. It returns
421    #' `FALSE` otherwise.
422
423    has_error_connection = function()
424      process_has_error_connection(self, private),
425
426    #' @description
427    #' `$has_poll_connection()` return `TRUE` if there is a poll connection,
428    #' `FALSE` otherwise.
429
430    has_poll_connection = function()
431      process_has_poll_connection(self, private),
432
433    #' @description
434    #' `$get_input_connection()` returns a connection object, to the
435    #' standard input stream of the process.
436
437    get_input_connection =  function()
438      process_get_input_connection(self, private),
439
440    #' @description
441    #' `$get_output_connection()` returns a connection object, to the
442    #' standard output stream of the process.
443
444    get_output_connection = function()
445      process_get_output_connection(self, private),
446
447    #' @description
448    #' `$get_error_conneciton()` returns a connection object, to the
449    #' standard error stream of the process.
450
451    get_error_connection = function()
452      process_get_error_connection(self, private),
453
454    #' @description
455    #' `$read_all_output()` waits for all standard output from the process.
456    #' It does not return until the process has finished.
457    #' Note that this process involves waiting for the process to finish,
458    #' polling for I/O and potentially several `readLines()` calls.
459    #' It returns a character scalar. This will return content only if
460    #' `stdout="|"` was used. Otherwise, it will throw an error.
461
462    read_all_output = function()
463      process_read_all_output(self, private),
464
465    #' @description
466    #' `$read_all_error()` waits for all standard error from the process.
467    #' It does not return until the process has finished.
468    #' Note that this process involves waiting for the process to finish,
469    #' polling for I/O and potentially several `readLines()` calls.
470    #' It returns a character scalar. This will return content only if
471    #' `stderr="|"` was used. Otherwise, it will throw an error.
472
473    read_all_error = function()
474      process_read_all_error(self, private),
475
476    #' @description
477    #' `$read_all_output_lines()` waits for all standard output lines
478    #' from a process. It does not return until the process has finished.
479    #' Note that this process involves waiting for the process to finish,
480    #' polling for I/O and potentially several `readLines()` calls.
481    #' It returns a character vector. This will return content only if
482    #' `stdout="|"` was used. Otherwise, it will throw an error.
483
484    read_all_output_lines = function()
485      process_read_all_output_lines(self, private),
486
487    #' @description
488    #' `$read_all_error_lines()` waits for all standard error lines from
489    #' a process. It does not return until the process has finished.
490    #' Note that this process involves waiting for the process to finish,
491    #' polling for I/O and potentially several `readLines()` calls.
492    #' It returns a character vector. This will return content only if
493    #' `stderr="|"` was used. Otherwise, it will throw an error.
494
495    read_all_error_lines = function()
496      process_read_all_error_lines(self, private),
497
498    #' @description
499    #' `$write_input()` writes the character vector (separated by `sep`) to
500    #' the standard input of the process. It will be converted to the specified
501    #' encoding. This operation is non-blocking, and it will return, even if
502    #' the write fails (because the write buffer is full), or if it suceeds
503    #' partially (i.e. not the full string is written). It returns with a raw
504    #' vector, that contains the bytes that were not written. You can supply
505    #' this raw vector to `$write_input()` again, until it is fully written,
506    #' and then the return value will be `raw(0)` (invisibly).
507    #'
508    #' @param str Character or raw vector to write to the standard input
509    #'   of the process. If a character vector with a marked encoding,
510    #'   it will be converted to `encoding`.
511    #' @param sep Separator to add between `str` elements if it is a
512    #'   character vector. It is ignored if `str` is a raw vector.
513    #' @return Leftover text (as a raw vector), that was not written.
514
515    write_input = function(str, sep = "\n")
516      process_write_input(self, private, str, sep),
517
518    #' @description
519    #' `$get_input_file()` if the `stdin` argument was a filename,
520    #' this returns the absolute path to the file. If `stdin` was `"|"` or
521    #' `NULL`, this simply returns that value.
522
523    get_input_file = function()
524      process_get_input_file(self, private),
525
526    #' @description
527    #' `$get_output_file()` if the `stdout` argument was a filename,
528    #' this returns the absolute path to the file. If `stdout` was `"|"` or
529    #' `NULL`, this simply returns that value.
530
531    get_output_file = function()
532      process_get_output_file(self, private),
533
534    #' @description
535    #' `$get_error_file()` if the `stderr` argument was a filename,
536    #' this returns the absolute path to the file. If `stderr` was `"|"` or
537    #' `NULL`, this simply returns that value.
538
539    get_error_file = function()
540      process_get_error_file(self, private),
541
542    #' @description
543    #' `$poll_io()` polls the process's connections for I/O. See more in
544    #' the _Polling_ section, and see also the [poll()] function
545    #' to poll on multiple processes.
546
547    poll_io = function(timeout)
548      process_poll_io(self, private, timeout),
549
550    #' @description
551    #' `$get_poll_connetion()` returns the poll connection, if the process has
552    #' one.
553
554    get_poll_connection = function()
555      process_get_poll_connection(self, private),
556
557    #' @description
558    #' `$get_result()` returns the result of the post processesing function.
559    #' It can only be called once the process has finished. If the process has
560    #' no post-processing function, then `NULL` is returned.
561
562    get_result = function()
563      process_get_result(self, private),
564
565    #' @description
566    #' `$as_ps_handle()` returns a [ps::ps_handle] object, corresponding to
567    #' the process.
568
569    as_ps_handle = function()
570      process_as_ps_handle(self, private),
571
572    #' @description
573    #' Calls [ps::ps_name()] to get the process name.
574
575    get_name = function()
576      ps_method(ps::ps_name, self),
577
578    #' @description
579    #' Calls [ps::ps_exe()] to get the path of the executable.
580
581    get_exe = function()
582      ps_method(ps::ps_exe, self),
583
584    #' @description
585    #' Calls [ps::ps_cmdline()] to get the command line.
586
587    get_cmdline = function()
588      ps_method(ps::ps_cmdline, self),
589
590    #' @description
591    #' Calls [ps::ps_status()] to get the process status.
592
593    get_status = function()
594      ps_method(ps::ps_status, self),
595
596    #' @description
597    #' calls [ps::ps_username()] to get the username.
598
599    get_username = function()
600      ps_method(ps::ps_username, self),
601
602    #' @description
603    #' Calls [ps::ps_cwd()] to get the current working directory.
604
605    get_wd = function()
606      ps_method(ps::ps_cwd, self),
607
608    #' @description
609    #' Calls [ps::ps_cpu_times()] to get CPU usage data.
610
611    get_cpu_times = function()
612      ps_method(ps::ps_cpu_times, self),
613
614    #' @description
615    #' Calls [ps::ps_memory_info()] to get memory data.
616
617    get_memory_info = function()
618      ps_method(ps::ps_memory_info, self),
619
620    #' @description
621    #' Calls [ps::ps_suspend()] to suspend the process.
622
623    suspend = function()
624      ps_method(ps::ps_suspend, self),
625
626    #' @description
627    #' Calls [ps::ps_resume()] to resume a suspended process.
628
629    resume = function()
630      ps_method(ps::ps_resume, self)
631  ),
632
633  private = list(
634
635    command = NULL,       # Save 'command' argument here
636    args = NULL,          # Save 'args' argument here
637    cleanup = NULL,       # cleanup argument
638    cleanup_tree = NULL,  # cleanup_tree argument
639    stdin = NULL,         # stdin argument or stream
640    stdout = NULL,        # stdout argument or stream
641    stderr = NULL,        # stderr argument or stream
642    pty = NULL,           # whether we should create a PTY
643    pty_options = NULL,   # various PTY options
644    pstdin = NULL,        # the original stdin argument
645    pstdout = NULL,       # the original stdout argument
646    pstderr = NULL,       # the original stderr argument
647    cleanfiles = NULL,    # which temp stdout/stderr file(s) to clean up
648    wd = NULL,            # working directory (or NULL for current)
649    starttime = NULL,     # timestamp of start
650    echo_cmd = NULL,      # whether to echo the command
651    windows_verbatim_args = NULL,
652    windows_hide_window = NULL,
653
654    status = NULL,        # C file handle
655
656    supervised = FALSE,   # Whether process is tracked by supervisor
657
658    stdin_pipe = NULL,
659    stdout_pipe = NULL,
660    stderr_pipe = NULL,
661    poll_pipe = NULL,
662
663    encoding = "",
664
665    env = NULL,
666
667    connections = list(),
668
669    post_process = NULL,
670    post_process_result = NULL,
671    post_process_done = FALSE,
672
673    tree_id = NULL,
674
675    get_short_name = function()
676      process_get_short_name(self, private),
677    close_connections = function()
678      process_close_connections(self, private)
679  )
680)
681
682## See the C source code for a discussion about the implementation
683## of these methods
684
685process_wait <- function(self, private, timeout) {
686  "!DEBUG process_wait `private$get_short_name()`"
687  rethrow_call_with_cleanup(
688    c_processx_wait, private$status,
689    as.integer(timeout),
690    private$get_short_name()
691  )
692  invisible(self)
693}
694
695process_is_alive <- function(self, private) {
696  "!DEBUG process_is_alive `private$get_short_name()`"
697  rethrow_call(c_processx_is_alive, private$status, private$get_short_name())
698}
699
700process_get_exit_status <- function(self, private) {
701  "!DEBUG process_get_exit_status `private$get_short_name()`"
702  rethrow_call(c_processx_get_exit_status, private$status,
703               private$get_short_name())
704}
705
706process_signal <- function(self, private, signal) {
707  "!DEBUG process_signal `private$get_short_name()` `signal`"
708  rethrow_call(c_processx_signal, private$status, as.integer(signal),
709               private$get_short_name())
710}
711
712process_interrupt <- function(self, private) {
713  "!DEBUG process_interrupt `private$get_short_name()`"
714  if (os_type() == "windows") {
715    pid <- as.character(self$get_pid())
716    st <- run(get_tool("interrupt"), c(pid, "c"), error_on_status = FALSE)
717    if (st$status == 0) TRUE else FALSE
718  } else {
719    rethrow_call(c_processx_interrupt, private$status,
720                 private$get_short_name())
721  }
722}
723
724process_kill <- function(self, private, grace, close_connections) {
725  "!DEBUG process_kill '`private$get_short_name()`', pid `self$get_pid()`"
726  ret <- rethrow_call(c_processx_kill, private$status, as.numeric(grace),
727                      private$get_short_name())
728  if (close_connections) private$close_connections()
729  ret
730}
731
732process_kill_tree <- function(self, private, grace, close_connections) {
733  "!DEBUG process_kill_tree '`private$get_short_name()`', pid `self$get_pid()`"
734  if (!ps::ps_is_supported()) {
735    throw(new_not_implemented_error(
736      "kill_tree is not supported on this platform"))
737  }
738
739  ret <- get("ps_kill_tree", asNamespace("ps"))(private$tree_id)
740  if (close_connections) private$close_connections()
741  ret
742}
743
744process_get_start_time <- function(self, private) {
745  format_unix_time(private$starttime)
746}
747
748process_get_pid <- function(self, private) {
749  rethrow_call(c_processx_get_pid, private$status)
750}
751
752process_is_supervised <- function(self, private) {
753  private$supervised
754}
755
756process_supervise <- function(self, private, status) {
757  if (status && !self$is_supervised()) {
758    supervisor_watch_pid(self$get_pid())
759    private$supervised <- TRUE
760
761  } else if (!status && self$is_supervised()) {
762    supervisor_unwatch_pid(self$get_pid())
763    private$supervised <- FALSE
764  }
765}
766
767process_get_result <- function(self, private) {
768  if (self$is_alive()) throw(new_error("Process is still alive"))
769  if (!private$post_process_done && is.function(private$post_process)) {
770    private$post_process_result <- private$post_process()
771    private$post_process_done <- TRUE
772  }
773  private$post_process_result
774}
775
776process_as_ps_handle <- function(self, private) {
777  ps::ps_handle(self$get_pid(), self$get_start_time())
778}
779
780ps_method <- function(fun, self) {
781  fun(ps::ps_handle(self$get_pid(), self$get_start_time()))
782}
783
784process_close_connections <- function(self, private) {
785  for (f in c("stdin_pipe", "stdout_pipe", "stderr_pipe", "poll_pipe")) {
786    if (!is.null(p <- private[[f]])) {
787      rethrow_call(c_processx_connection_close, p)
788    }
789  }
790}
791
792#' Default options for pseudo terminals (ptys)
793#'
794#' @return Named list of default values of pty options.
795#'
796#' Options and default values:
797#' * `echo` whether to keep the echo on the terminal. `FALSE` turns echo
798#'   off.
799#' * `rows` the (initial) terminal size, number of rows.
800#' * `cols` the (initial) terminal size, number of columns.
801#'
802#' @export
803
804default_pty_options <- function() {
805  list(
806    echo = FALSE,
807    rows = 25L,
808    cols = 80L
809  )
810}
811