1 /* exechelp-w32.c - Fork and exec helpers for W32.
2 * Copyright (C) 2004, 2007, 2008, 2009,
3 * 2010 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
7 * This file is free software; you can redistribute it and/or modify
8 * it under the terms of either
10 * - the GNU Lesser General Public License as published by the Free
11 * Software Foundation; either version 3 of the License, or (at
12 * your option) any later version.
16 * - the GNU General Public License as published by the Free
17 * Software Foundation; either version 2 of the License, or (at
18 * your option) any later version.
20 * or both in parallel, as here.
22 * This file is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, see <https://www.gnu.org/licenses/>.
33 #if !defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM)
34 #error This code is only used on W32.
48 #ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */
58 # include <sys/stat.h>
67 /* Define to 1 do enable debugging. */
68 #define DEBUG_W32_SPAWN 0
71 /* It seems Vista doesn't grok X_OK and so fails access() tests.
72 Previous versions interpreted X_OK as F_OK anyway, so we'll just
77 /* We assume that a HANDLE can be represented by an int which should
78 be true for all i386 systems (HANDLE is defined as void *) and
79 these are the only systems for which Windows is available. Further
80 we assume that -1 denotes an invalid handle. */
81 # define fd_to_handle(a) ((HANDLE)(a))
82 # define handle_to_fd(a) ((int)(a))
83 # define pid_to_handle(a) ((HANDLE)(a))
84 # define handle_to_pid(a) ((int)(a))
88 static inline gpg_error_t
89 my_error_from_syserror (void)
91 return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
94 static inline gpg_error_t
95 my_error (int errcode)
97 return gpg_err_make (default_errsource, errcode);
101 /* Return the maximum number of currently allowed open file
102 descriptors. Only useful on POSIX systems but returns a value on
103 other systems too. */
115 max_fds = 256; /* Arbitrary limit. */
121 /* Under Windows this is a dummy function. */
123 close_all_fds (int first, int *except)
130 /* Returns an array with all currently open file descriptors. The end
131 of the array is marked by -1. The caller needs to release this
132 array using the *standard free* and not with xfree. This allow the
133 use of this function right at startup even before libgcrypt has
134 been initialized. Returns NULL on error and sets ERRNO
137 get_all_open_fds (void)
143 array = calloc (1, sizeof *array);
149 max_fd = get_max_fds ();
150 narray = 32; /* If you change this change also t-exechelp.c. */
151 array = calloc (narray, sizeof *array);
155 /* Note: The list we return is ordered. */
156 for (idx=0, fd=0; fd < max_fd; fd++)
157 if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
163 narray += (narray < 256)? 32:256;
164 tmp = realloc (array, narray * sizeof *array);
180 /* Helper function to build_w32_commandline. */
182 build_w32_commandline_copy (char *buffer, const char *string)
187 if (!*string) /* Empty string. */
188 p = stpcpy (p, "\"\"");
189 else if (strpbrk (string, " \t\n\v\f\""))
191 /* Need to do some kind of quoting. */
192 p = stpcpy (p, "\"");
193 for (s=string; *s; s++)
203 p = stpcpy (p, string);
208 /* Build a command line for use with W32's CreateProcess. On success
209 CMDLINE gets the address of a newly allocated string. */
211 build_w32_commandline (const char *pgmname, const char * const *argv,
221 n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
224 n++; /* Need to double inner quotes. */
225 for (i=0; (s=argv[i]); i++)
227 n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
230 n++; /* Need to double inner quotes. */
234 buf = p = xtrymalloc (n);
236 return my_error_from_syserror ();
238 p = build_w32_commandline_copy (p, pgmname);
239 for (i=0; argv[i]; i++)
242 p = build_w32_commandline_copy (p, argv[i]);
250 #define INHERIT_READ 1
251 #define INHERIT_WRITE 2
252 #define INHERIT_BOTH (INHERIT_READ|INHERIT_WRITE)
254 /* Create pipe. FLAGS indicates which ends are inheritable. */
256 create_inheritable_pipe (HANDLE filedes[2], int flags)
259 SECURITY_ATTRIBUTES sec_attr;
261 memset (&sec_attr, 0, sizeof sec_attr );
262 sec_attr.nLength = sizeof sec_attr;
263 sec_attr.bInheritHandle = TRUE;
265 if (!CreatePipe (&r, &w, &sec_attr, 0))
268 if ((flags & INHERIT_READ) == 0)
269 if (! SetHandleInformation (r, HANDLE_FLAG_INHERIT, 0))
272 if ((flags & INHERIT_WRITE) == 0)
273 if (! SetHandleInformation (w, HANDLE_FLAG_INHERIT, 0))
281 log_error ("SetHandleInformation failed: %s\n", w32_strerror (-1));
289 w32_open_null (int for_write)
293 hfile = CreateFileW (L"nul",
294 for_write? GENERIC_WRITE : GENERIC_READ,
295 FILE_SHARE_READ | FILE_SHARE_WRITE,
296 NULL, OPEN_EXISTING, 0, NULL);
297 if (hfile == INVALID_HANDLE_VALUE)
298 log_debug ("can't open 'nul': %s\n", w32_strerror (-1));
304 create_pipe_and_estream (int filedes[2], int flags,
305 estream_t *r_fp, int outbound, int nonblock)
310 filedes[0] = filedes[1] = -1;
311 err = my_error (GPG_ERR_GENERAL);
312 if (!create_inheritable_pipe (fds, flags))
314 filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), O_RDONLY);
315 if (filedes[0] == -1)
317 log_error ("failed to translate osfhandle %p\n", fds[0]);
318 CloseHandle (fds[1]);
322 filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), O_APPEND);
323 if (filedes[1] == -1)
325 log_error ("failed to translate osfhandle %p\n", fds[1]);
328 CloseHandle (fds[1]);
338 *r_fp = es_fdopen (filedes[0], nonblock? "r,nonblock" : "r");
340 *r_fp = es_fdopen (filedes[1], nonblock? "w,nonblock" : "w");
343 err = my_error_from_syserror ();
344 log_error (_("error creating a stream for a pipe: %s\n"),
348 filedes[0] = filedes[1] = -1;
356 /* Portable function to create a pipe. Under Windows the write end is
357 inheritable. If R_FP is not NULL, an estream is created for the
358 read end and stored at R_FP. */
360 gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
362 return create_pipe_and_estream (filedes, INHERIT_WRITE,
367 /* Portable function to create a pipe. Under Windows the read end is
368 inheritable. If R_FP is not NULL, an estream is created for the
369 write end and stored at R_FP. */
371 gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock)
373 return create_pipe_and_estream (filedes, INHERIT_READ,
378 /* Portable function to create a pipe. Under Windows both ends are
381 gnupg_create_pipe (int filedes[2])
383 return create_pipe_and_estream (filedes, INHERIT_BOTH,
388 /* Fork and exec the PGMNAME, see exechelp.h for details. */
390 gnupg_spawn_process (const char *pgmname, const char *argv[],
391 int *except, void (*preexec)(void), unsigned int flags,
398 SECURITY_ATTRIBUTES sec_attr;
399 PROCESS_INFORMATION pi =
401 NULL, /* Returns process handle. */
402 0, /* Returns primary thread handle. */
403 0, /* Returns pid. */
409 HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
410 HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
411 HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
412 estream_t infp = NULL;
413 estream_t outfp = NULL;
414 estream_t errfp = NULL;
415 HANDLE nullhd[3] = {INVALID_HANDLE_VALUE,
416 INVALID_HANDLE_VALUE,
417 INVALID_HANDLE_VALUE};
420 gpg_err_source_t errsource = default_errsource;
421 int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK);
423 (void)except; /* Not yet used. */
431 *pid = (pid_t)(-1); /* Always required. */
435 if (create_inheritable_pipe (inpipe, INHERIT_READ))
437 err = gpg_err_make (errsource, GPG_ERR_GENERAL);
438 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
442 syshd.type = ES_SYSHD_HANDLE;
443 syshd.u.handle = inpipe[1];
444 infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w");
447 err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
448 log_error (_("error creating a stream for a pipe: %s\n"),
450 CloseHandle (inpipe[0]);
451 CloseHandle (inpipe[1]);
452 inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE;
459 if (create_inheritable_pipe (outpipe, INHERIT_WRITE))
461 err = gpg_err_make (errsource, GPG_ERR_GENERAL);
462 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
466 syshd.type = ES_SYSHD_HANDLE;
467 syshd.u.handle = outpipe[0];
468 outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
471 err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
472 log_error (_("error creating a stream for a pipe: %s\n"),
474 CloseHandle (outpipe[0]);
475 CloseHandle (outpipe[1]);
476 outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE;
479 else if (inpipe[1] != INVALID_HANDLE_VALUE)
480 CloseHandle (inpipe[1]);
481 if (inpipe[0] != INVALID_HANDLE_VALUE)
482 CloseHandle (inpipe[0]);
489 if (create_inheritable_pipe (errpipe, INHERIT_WRITE))
491 err = gpg_err_make (errsource, GPG_ERR_GENERAL);
492 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
496 syshd.type = ES_SYSHD_HANDLE;
497 syshd.u.handle = errpipe[0];
498 errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r");
501 err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
502 log_error (_("error creating a stream for a pipe: %s\n"),
504 CloseHandle (errpipe[0]);
505 CloseHandle (errpipe[1]);
506 errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE;
509 else if (outpipe[0] != INVALID_HANDLE_VALUE)
510 CloseHandle (outpipe[0]);
511 if (outpipe[1] != INVALID_HANDLE_VALUE)
512 CloseHandle (outpipe[1]);
515 else if (inpipe[1] != INVALID_HANDLE_VALUE)
516 CloseHandle (inpipe[1]);
517 if (inpipe[0] != INVALID_HANDLE_VALUE)
518 CloseHandle (inpipe[0]);
523 /* Prepare security attributes. */
524 memset (&sec_attr, 0, sizeof sec_attr );
525 sec_attr.nLength = sizeof sec_attr;
526 sec_attr.bInheritHandle = FALSE;
528 /* Build the command line. */
529 err = build_w32_commandline (pgmname, argv, &cmdline);
533 if (inpipe[0] == INVALID_HANDLE_VALUE)
534 nullhd[0] = w32_open_null (0);
535 if (outpipe[1] == INVALID_HANDLE_VALUE)
536 nullhd[1] = w32_open_null (1);
537 if (errpipe[1] == INVALID_HANDLE_VALUE)
538 nullhd[2] = w32_open_null (1);
540 /* Start the process. Note that we can't run the PREEXEC function
541 because this might change our own environment. */
544 memset (&si, 0, sizeof si);
546 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
547 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
548 si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0];
549 si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1];
550 si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1];
552 cr_flags = (CREATE_DEFAULT_ERROR_MODE
553 | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0)
554 | GetPriorityClass (GetCurrentProcess ())
556 /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
557 if (!CreateProcess (pgmname, /* Program to start. */
558 cmdline, /* Command line arguments. */
559 &sec_attr, /* Process security attributes. */
560 &sec_attr, /* Thread security attributes. */
561 TRUE, /* Inherit handles. */
562 cr_flags, /* Creation flags. */
563 NULL, /* Environment. */
564 NULL, /* Use current drive/directory. */
565 &si, /* Startup information. */
566 &pi /* Returns process information. */
569 log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
573 else if (inpipe[1] != INVALID_HANDLE_VALUE)
574 CloseHandle (outpipe[1]);
575 if (inpipe[0] != INVALID_HANDLE_VALUE)
576 CloseHandle (inpipe[0]);
579 else if (outpipe[0] != INVALID_HANDLE_VALUE)
580 CloseHandle (outpipe[0]);
581 if (outpipe[1] != INVALID_HANDLE_VALUE)
582 CloseHandle (outpipe[1]);
585 else if (errpipe[0] != INVALID_HANDLE_VALUE)
586 CloseHandle (errpipe[0]);
587 if (errpipe[1] != INVALID_HANDLE_VALUE)
588 CloseHandle (errpipe[1]);
589 return gpg_err_make (errsource, GPG_ERR_GENERAL);
594 /* Close the inherited handles to /dev/null. */
595 for (i=0; i < DIM (nullhd); i++)
596 if (nullhd[i] != INVALID_HANDLE_VALUE)
597 CloseHandle (nullhd[i]);
599 /* Close the inherited ends of the pipes. */
600 if (inpipe[0] != INVALID_HANDLE_VALUE)
601 CloseHandle (inpipe[0]);
602 if (outpipe[1] != INVALID_HANDLE_VALUE)
603 CloseHandle (outpipe[1]);
604 if (errpipe[1] != INVALID_HANDLE_VALUE)
605 CloseHandle (errpipe[1]);
607 /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
608 /* " dwProcessID=%d dwThreadId=%d\n", */
609 /* pi.hProcess, pi.hThread, */
610 /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
611 /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */
613 /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
614 invalid argument error if we pass it the correct processID. As a
615 workaround we use -1 (ASFW_ANY). */
616 if ((flags & GNUPG_SPAWN_RUN_ASFW))
617 gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
619 /* Process has been created suspended; resume it now. */
620 ResumeThread (pi.hThread);
621 CloseHandle (pi.hThread);
630 *pid = handle_to_pid (pi.hProcess);
637 /* Simplified version of gnupg_spawn_process. This function forks and
638 then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
639 and ERRFD to stderr (any of them may be -1 to connect them to
640 /dev/null). The arguments for the process are expected in the NULL
641 terminated array ARGV. The program name itself should not be
642 included there. Calling gnupg_wait_process is required.
644 Returns 0 on success or an error code. */
646 gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
647 int infd, int outfd, int errfd, pid_t *pid)
650 SECURITY_ATTRIBUTES sec_attr;
651 PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
657 /* Setup return values. */
660 /* Prepare security attributes. */
661 memset (&sec_attr, 0, sizeof sec_attr );
662 sec_attr.nLength = sizeof sec_attr;
663 sec_attr.bInheritHandle = FALSE;
665 /* Build the command line. */
666 err = build_w32_commandline (pgmname, argv, &cmdline);
670 memset (&si, 0, sizeof si);
672 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
673 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
674 stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
675 stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
676 stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
677 si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd);
678 si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
679 si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
681 /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */
682 if (!CreateProcess (pgmname, /* Program to start. */
683 cmdline, /* Command line arguments. */
684 &sec_attr, /* Process security attributes. */
685 &sec_attr, /* Thread security attributes. */
686 TRUE, /* Inherit handles. */
687 (CREATE_DEFAULT_ERROR_MODE
688 | GetPriorityClass (GetCurrentProcess ())
689 | CREATE_SUSPENDED | DETACHED_PROCESS),
690 NULL, /* Environment. */
691 NULL, /* Use current drive/directory. */
692 &si, /* Startup information. */
693 &pi /* Returns process information. */
696 log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
697 err = my_error (GPG_ERR_GENERAL);
702 for (i=0; i < 3; i++)
703 if (stdhd[i] != INVALID_HANDLE_VALUE)
704 CloseHandle (stdhd[i]);
708 /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */
709 /* " dwProcessID=%d dwThreadId=%d\n", */
710 /* pi.hProcess, pi.hThread, */
711 /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
713 /* Process has been created suspended; resume it now. */
714 ResumeThread (pi.hThread);
715 CloseHandle (pi.hThread);
717 *pid = handle_to_pid (pi.hProcess);
723 /* See exechelp.h for a description. */
725 gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode)
727 return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode);
730 /* See exechelp.h for a description. */
732 gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count,
733 int hang, int *r_exitcodes)
735 gpg_err_code_t ec = 0;
740 procs = xtrycalloc (count, sizeof *procs);
742 return my_error_from_syserror ();
744 for (i = 0; i < count; i++)
749 if (pids[i] == (pid_t)(-1))
750 return my_error (GPG_ERR_INV_VALUE);
752 procs[i] = fd_to_handle (pids[i]);
755 /* FIXME: We should do a pth_waitpid here. However this has not yet
756 been implemented. A special W32 pth system call would even be
758 code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0);
762 ec = GPG_ERR_TIMEOUT;
766 log_error (_("waiting for processes to terminate failed: %s\n"),
768 ec = GPG_ERR_GENERAL;
772 for (i = 0; i < count; i++)
776 if (! GetExitCodeProcess (procs[i], &exc))
778 log_error (_("error getting exit code of process %d: %s\n"),
779 (int) pids[i], w32_strerror (-1) );
780 ec = GPG_ERR_GENERAL;
785 log_error (_("error running '%s': exit status %d\n"),
786 pgmnames[i], (int)exc);
788 r_exitcodes[i] = (int)exc;
789 ec = GPG_ERR_GENERAL;
800 log_error ("WaitForMultipleObjects returned unexpected "
802 ec = GPG_ERR_GENERAL;
807 return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
813 gnupg_release_process (pid_t pid)
815 if (pid != (pid_t)INVALID_HANDLE_VALUE)
817 HANDLE process = (HANDLE)pid;
819 CloseHandle (process);
824 /* Spawn a new process and immediately detach from it. The name of
825 the program to exec is PGMNAME and its arguments are in ARGV (the
826 programname is automatically passed as first argument).
827 Environment strings in ENVP are set. An error is returned if
828 pgmname is not executable; to make this work it is necessary to
829 provide an absolute file name. All standard file descriptors are
830 connected to /dev/null. */
832 gnupg_spawn_process_detached (const char *pgmname, const char *argv[],
836 SECURITY_ATTRIBUTES sec_attr;
837 PROCESS_INFORMATION pi =
839 NULL, /* Returns process handle. */
840 0, /* Returns primary thread handle. */
841 0, /* Returns pid. */
849 /* We don't use ENVP. */
852 if (access (pgmname, X_OK))
853 return my_error_from_syserror ();
855 /* Prepare security attributes. */
856 memset (&sec_attr, 0, sizeof sec_attr );
857 sec_attr.nLength = sizeof sec_attr;
858 sec_attr.bInheritHandle = FALSE;
860 /* Build the command line. */
861 err = build_w32_commandline (pgmname, argv, &cmdline);
865 /* Start the process. */
866 memset (&si, 0, sizeof si);
868 si.dwFlags = STARTF_USESHOWWINDOW;
869 si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
871 cr_flags = (CREATE_DEFAULT_ERROR_MODE
872 | GetPriorityClass (GetCurrentProcess ())
873 | CREATE_NEW_PROCESS_GROUP
875 /* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */
876 /* pgmname, cmdline); */
877 if (!CreateProcess (pgmname, /* Program to start. */
878 cmdline, /* Command line arguments. */
879 &sec_attr, /* Process security attributes. */
880 &sec_attr, /* Thread security attributes. */
881 FALSE, /* Inherit handles. */
882 cr_flags, /* Creation flags. */
883 NULL, /* Environment. */
884 NULL, /* Use current drive/directory. */
885 &si, /* Startup information. */
886 &pi /* Returns process information. */
889 log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1));
891 return my_error (GPG_ERR_GENERAL);
896 /* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */
897 /* " dwProcessID=%d dwThreadId=%d\n", */
898 /* pi.hProcess, pi.hThread, */
899 /* (int) pi.dwProcessId, (int) pi.dwThreadId); */
901 CloseHandle (pi.hThread);
902 CloseHandle (pi.hProcess);
908 /* Kill a process; that is send an appropriate signal to the process.
909 gnupg_wait_process must be called to actually remove the process
910 from the system. An invalid PID is ignored. */
912 gnupg_kill_process (pid_t pid)
914 if (pid != (pid_t) INVALID_HANDLE_VALUE)
916 HANDLE process = (HANDLE) pid;
918 /* Arbitrary error code. */
919 TerminateProcess (process, 1);