3 #include <process.h> /* for msvc _beginthreadex, _endthreadex */
10 static char *make_command_line( char *shell_name
, char *exec_path
, char **argv
);
12 typedef struct sub_process_t
{
20 volatile DWORD outcnt
;
22 volatile DWORD errcnt
;
30 /* keep track of children so we can implement a waitpid-like routine */
31 static sub_process
*proc_array
[256];
32 static int proc_index
= 0;
33 static int fake_exits_pending
= 0;
36 * When a process has been waited for, adjust the wait state
37 * array so that we don't wait for it again
40 process_adjust_wait_state(sub_process
* pproc
)
47 for (i
= 0; i
< proc_index
; i
++)
48 if (proc_array
[i
]->pid
== pproc
->pid
)
54 memmove(&proc_array
[i
], &proc_array
[i
+1],
55 (proc_index
-i
) * sizeof(sub_process
*));
56 proc_array
[proc_index
] = NULL
;
61 * Waits for any of the registered child processes to finish.
64 process_wait_for_any_private(void)
73 /* build array of handles to wait for */
74 for (i
= 0; i
< proc_index
; i
++) {
75 handles
[i
] = (HANDLE
) proc_array
[i
]->pid
;
77 if (fake_exits_pending
&& proc_array
[i
]->exit_code
)
81 /* wait for someone to exit */
82 if (!fake_exits_pending
) {
83 retval
= WaitForMultipleObjects(proc_index
, handles
, FALSE
, INFINITE
);
84 which
= retval
- WAIT_OBJECT_0
;
87 retval
= !WAIT_FAILED
;
91 /* return pointer to process */
92 if (retval
!= WAIT_FAILED
) {
93 sub_process
* pproc
= proc_array
[which
];
94 process_adjust_wait_state(pproc
);
101 * Terminate a process.
104 process_kill(HANDLE proc
, int signal
)
106 sub_process
* pproc
= (sub_process
*) proc
;
107 pproc
->signal
= signal
;
108 return (TerminateProcess((HANDLE
) pproc
->pid
, signal
));
112 * Use this function to register processes you wish to wait for by
113 * calling process_file_io(NULL) or process_wait_any(). This must be done
114 * because it is possible for callers of this library to reuse the same
115 * handle for multiple processes launches :-(
118 process_register(HANDLE proc
)
120 proc_array
[proc_index
++] = (sub_process
*) proc
;
124 * Public function which works kind of like waitpid(). Wait for any
125 * of the children to die and return results. To call this function,
126 * you must do 1 of things:
128 * x = process_easy(...);
132 * x = process_init_fd();
133 * process_register(x);
137 * x = process_init();
138 * process_register(x);
140 * You must NOT then call process_pipe_io() because this function is
141 * not capable of handling automatic notification of any child
146 process_wait_for_any(void)
148 sub_process
* pproc
= process_wait_for_any_private();
154 * Ouch! can't tell caller if this fails directly. Caller
155 * will have to use process_last_err()
157 (void) process_file_io(pproc
);
158 return ((HANDLE
) pproc
);
163 process_errno(HANDLE proc
)
165 return (((sub_process
*)proc
)->lerrno
);
169 process_signal(HANDLE proc
)
171 return (((sub_process
*)proc
)->signal
);
175 process_last_err(HANDLE proc
)
177 return (((sub_process
*)proc
)->last_err
);
181 process_exit_code(HANDLE proc
)
183 return (((sub_process
*)proc
)->exit_code
);
187 process_outbuf(HANDLE proc
)
189 return (((sub_process
*)proc
)->outp
);
193 process_errbuf(HANDLE proc
)
195 return (((sub_process
*)proc
)->errp
);
199 process_outcnt(HANDLE proc
)
201 return (((sub_process
*)proc
)->outcnt
);
205 process_errcnt(HANDLE proc
)
207 return (((sub_process
*)proc
)->errcnt
);
211 process_pipes(HANDLE proc
, int pipes
[3])
213 pipes
[0] = ((sub_process
*)proc
)->sv_stdin
[0];
214 pipes
[1] = ((sub_process
*)proc
)->sv_stdout
[0];
215 pipes
[2] = ((sub_process
*)proc
)->sv_stderr
[0];
225 * open file descriptors for attaching stdin/stdout/sterr
227 HANDLE stdin_pipes
[2];
228 HANDLE stdout_pipes
[2];
229 HANDLE stderr_pipes
[2];
230 SECURITY_ATTRIBUTES inherit
;
231 BYTE sd
[SECURITY_DESCRIPTOR_MIN_LENGTH
];
233 pproc
= malloc(sizeof(*pproc
));
234 memset(pproc
, 0, sizeof(*pproc
));
236 /* We can't use NULL for lpSecurityDescriptor because that
237 uses the default security descriptor of the calling process.
238 Instead we use a security descriptor with no DACL. This
239 allows nonrestricted access to the associated objects. */
241 if (!InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR
)(&sd
),
242 SECURITY_DESCRIPTOR_REVISION
)) {
243 pproc
->last_err
= GetLastError();
244 pproc
->lerrno
= E_SCALL
;
245 return((HANDLE
)pproc
);
248 inherit
.nLength
= sizeof(inherit
);
249 inherit
.lpSecurityDescriptor
= (PSECURITY_DESCRIPTOR
)(&sd
);
250 inherit
.bInheritHandle
= TRUE
;
252 // By convention, parent gets pipe[0], and child gets pipe[1]
253 // This means the READ side of stdin pipe goes into pipe[1]
254 // and the WRITE side of the stdout and stderr pipes go into pipe[1]
255 if (CreatePipe( &stdin_pipes
[1], &stdin_pipes
[0], &inherit
, 0) == FALSE
||
256 CreatePipe( &stdout_pipes
[0], &stdout_pipes
[1], &inherit
, 0) == FALSE
||
257 CreatePipe( &stderr_pipes
[0], &stderr_pipes
[1], &inherit
, 0) == FALSE
) {
259 pproc
->last_err
= GetLastError();
260 pproc
->lerrno
= E_SCALL
;
261 return((HANDLE
)pproc
);
265 // Mark the parent sides of the pipes as non-inheritable
267 if (SetHandleInformation(stdin_pipes
[0],
268 HANDLE_FLAG_INHERIT
, 0) == FALSE
||
269 SetHandleInformation(stdout_pipes
[0],
270 HANDLE_FLAG_INHERIT
, 0) == FALSE
||
271 SetHandleInformation(stderr_pipes
[0],
272 HANDLE_FLAG_INHERIT
, 0) == FALSE
) {
274 pproc
->last_err
= GetLastError();
275 pproc
->lerrno
= E_SCALL
;
276 return((HANDLE
)pproc
);
278 pproc
->sv_stdin
[0] = (int) stdin_pipes
[0];
279 pproc
->sv_stdin
[1] = (int) stdin_pipes
[1];
280 pproc
->sv_stdout
[0] = (int) stdout_pipes
[0];
281 pproc
->sv_stdout
[1] = (int) stdout_pipes
[1];
282 pproc
->sv_stderr
[0] = (int) stderr_pipes
[0];
283 pproc
->sv_stderr
[1] = (int) stderr_pipes
[1];
285 pproc
->using_pipes
= 1;
289 return((HANDLE
)pproc
);
294 process_init_fd(HANDLE stdinh
, HANDLE stdouth
, HANDLE stderrh
)
298 pproc
= malloc(sizeof(*pproc
));
299 memset(pproc
, 0, sizeof(*pproc
));
302 * Just pass the provided file handles to the 'child side' of the
303 * pipe, bypassing pipes altogether.
305 pproc
->sv_stdin
[1] = (int) stdinh
;
306 pproc
->sv_stdout
[1] = (int) stdouth
;
307 pproc
->sv_stderr
[1] = (int) stderrh
;
309 pproc
->last_err
= pproc
->lerrno
= 0;
311 return((HANDLE
)pproc
);
316 find_file(char *exec_path
, LPOFSTRUCT file_info
)
322 if ((exec_handle
= (HANDLE
)OpenFile(exec_path
, file_info
,
323 OF_READ
| OF_SHARE_COMPAT
)) != (HANDLE
)HFILE_ERROR
) {
327 fname
= malloc(strlen(exec_path
) + 5);
328 strcpy(fname
, exec_path
);
329 ext
= fname
+ strlen(fname
);
331 if ((exec_handle
= (HANDLE
)OpenFile(fname
, file_info
,
332 OF_READ
| OF_SHARE_COMPAT
)) != (HANDLE
)HFILE_ERROR
) {
338 if ((exec_handle
= (HANDLE
)OpenFile(fname
, file_info
,
339 OF_READ
| OF_SHARE_COMPAT
)) != (HANDLE
)HFILE_ERROR
) {
345 if ((exec_handle
= (HANDLE
)OpenFile(fname
, file_info
,
346 OF_READ
| OF_SHARE_COMPAT
)) != (HANDLE
)HFILE_ERROR
) {
357 * Description: Create the child process to be helped
361 * Notes/Dependencies:
371 sub_process
*pproc
= (sub_process
*)proc
;
372 char *shell_name
= 0;
373 int file_not_found
=0;
376 DWORD bytes_returned
;
379 STARTUPINFO startInfo
;
380 PROCESS_INFORMATION procInfo
;
386 * Shell script detection... if the exec_path starts with #! then
387 * we want to exec shell-script-name exec-path, not just exec-path
388 * NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl. We do not
389 * hard-code the path to the shell or perl or whatever: Instead, we
390 * assume it's in the path somewhere (generally, the NT tools
392 * We use OpenFile here because it is capable of searching the Path.
395 exec_handle
= find_file(exec_path
, &file_info
);
398 * If we couldn't open the file, just assume that Win32 will be able
399 * to find and execute it.
401 if (exec_handle
== (HANDLE
)HFILE_ERROR
) {
405 /* Attempt to read the first line of the file */
406 if (ReadFile( exec_handle
,
407 buf
, sizeof(buf
) - 1, /* leave room for trailing NULL */
408 &bytes_returned
, 0) == FALSE
|| bytes_returned
< 2) {
410 pproc
->last_err
= GetLastError();
411 pproc
->lerrno
= E_IO
;
412 CloseHandle(exec_handle
);
415 if (buf
[0] == '#' && buf
[1] == '!') {
417 * This is a shell script... Change the command line from
418 * exec_path args to shell_name exec_path args
422 /* Make sure buf is NULL terminated */
423 buf
[bytes_returned
] = 0;
425 * Depending on the file system type, etc. the first line
426 * of the shell script may end with newline or newline-carriage-return
427 * Whatever it ends with, cut it off.
429 p
= strchr(buf
, '\n');
432 p
= strchr(buf
, '\r');
437 * Find base name of shell
439 shell_name
= strrchr( buf
, '/');
443 shell_name
= &buf
[2];/* skipping "#!" */
447 CloseHandle(exec_handle
);
453 command_line
= make_command_line( shell_name
, exec_path
, argv
);
455 command_line
= make_command_line( shell_name
, file_info
.szPathName
,
458 if ( command_line
== NULL
) {
460 pproc
->lerrno
= E_NO_MEM
;
465 if (arr2envblk(envp
, &envblk
) ==FALSE
) {
467 pproc
->lerrno
= E_NO_MEM
;
468 free( command_line
);
473 if ((shell_name
) || (file_not_found
)) {
474 exec_path
= 0; /* Search for the program in %Path% */
476 exec_path
= file_info
.szPathName
;
480 * Set up inherited stdin, stdout, stderr for child
482 GetStartupInfo(&startInfo
);
483 startInfo
.dwFlags
= STARTF_USESTDHANDLES
;
484 startInfo
.lpReserved
= 0;
485 startInfo
.cbReserved2
= 0;
486 startInfo
.lpReserved2
= 0;
487 startInfo
.lpTitle
= shell_name
? shell_name
: exec_path
;
488 startInfo
.hStdInput
= (HANDLE
)pproc
->sv_stdin
[1];
489 startInfo
.hStdOutput
= (HANDLE
)pproc
->sv_stdout
[1];
490 startInfo
.hStdError
= (HANDLE
)pproc
->sv_stderr
[1];
493 * See if we need to setuid to a different user.
506 0, /* default security attributes for thread */
507 TRUE
, /* inherit handles (e.g. helper pipes, oserv socket) */
510 0, /* default starting directory */
512 &procInfo
) == FALSE
) {
514 pproc
->last_err
= GetLastError();
515 pproc
->lerrno
= E_FORK
;
516 fprintf(stderr
, "process_begin: CreateProcess(%s, %s, ...) failed.\n", exec_path
, command_line
);
517 free( command_line
);
522 pproc
->pid
= (int)procInfo
.hProcess
;
523 /* Close the thread handle -- we'll just watch the process */
524 CloseHandle(procInfo
.hThread
);
526 /* Close the halves of the pipes we don't need */
527 if (pproc
->sv_stdin
) {
528 CloseHandle((HANDLE
)pproc
->sv_stdin
[1]);
529 (HANDLE
)pproc
->sv_stdin
[1] = 0;
531 if (pproc
->sv_stdout
) {
532 CloseHandle((HANDLE
)pproc
->sv_stdout
[1]);
533 (HANDLE
)pproc
->sv_stdout
[1] = 0;
535 if (pproc
->sv_stderr
) {
536 CloseHandle((HANDLE
)pproc
->sv_stderr
[1]);
537 (HANDLE
)pproc
->sv_stderr
[1] = 0;
540 free( command_line
);
548 proc_stdin_thread(sub_process
*pproc
)
552 if (WriteFile( (HANDLE
) pproc
->sv_stdin
[0], pproc
->inp
, pproc
->incnt
,
553 &in_done
, NULL
) == FALSE
)
555 // This if should never be true for anonymous pipes, but gives
556 // us a chance to change I/O mechanisms later
557 if (in_done
< pproc
->incnt
) {
558 pproc
->incnt
-= in_done
;
559 pproc
->inp
+= in_done
;
564 return 0; // for compiler warnings only.. not reached
568 proc_stdout_thread(sub_process
*pproc
)
570 DWORD bufsize
= 1024;
573 pproc
->outp
= malloc(bufsize
);
574 if (pproc
->outp
== NULL
)
579 if (ReadFile( (HANDLE
)pproc
->sv_stdout
[0], &c
, 1, &nread
, NULL
)
581 /* map_win32_error_to_string(GetLastError());*/
586 if (pproc
->outcnt
+ nread
> bufsize
) {
587 bufsize
+= nread
+ 512;
588 pproc
->outp
= realloc(pproc
->outp
, bufsize
);
589 if (pproc
->outp
== NULL
) {
594 pproc
->outp
[pproc
->outcnt
++] = c
;
600 proc_stderr_thread(sub_process
*pproc
)
602 DWORD bufsize
= 1024;
605 pproc
->errp
= malloc(bufsize
);
606 if (pproc
->errp
== NULL
)
611 if (ReadFile( (HANDLE
)pproc
->sv_stderr
[0], &c
, 1, &nread
, NULL
) == FALSE
) {
612 map_win32_error_to_string(GetLastError());
617 if (pproc
->errcnt
+ nread
> bufsize
) {
618 bufsize
+= nread
+ 512;
619 pproc
->errp
= realloc(pproc
->errp
, bufsize
);
620 if (pproc
->errp
== NULL
) {
625 pproc
->errp
[pproc
->errcnt
++] = c
;
632 * Purpose: collects output from child process and returns results
638 * Notes/Dependencies:
646 sub_process
*pproc
= (sub_process
*)proc
;
647 bool_t stdin_eof
= FALSE
, stdout_eof
= FALSE
, stderr_eof
= FALSE
;
648 HANDLE childhand
= (HANDLE
) pproc
->pid
;
649 HANDLE tStdin
, tStdout
, tStderr
;
650 DWORD dwStdin
, dwStdout
, dwStderr
;
655 bool_t child_dead
= FALSE
;
659 * Create stdin thread, if needed
661 pproc
->inp
= stdin_data
;
662 pproc
->incnt
= stdin_data_len
;
665 CloseHandle((HANDLE
)pproc
->sv_stdin
[0]);
666 (HANDLE
)pproc
->sv_stdin
[0] = 0;
668 tStdin
= (HANDLE
) _beginthreadex( 0, 1024,
669 (unsigned (__stdcall
*) (void *))proc_stdin_thread
, pproc
, 0,
670 (unsigned int *) &dwStdin
);
672 pproc
->last_err
= GetLastError();
673 pproc
->lerrno
= E_SCALL
;
679 * Assume child will produce stdout and stderr
681 tStdout
= (HANDLE
) _beginthreadex( 0, 1024,
682 (unsigned (__stdcall
*) (void *))proc_stdout_thread
, pproc
, 0,
683 (unsigned int *) &dwStdout
);
684 tStderr
= (HANDLE
) _beginthreadex( 0, 1024,
685 (unsigned (__stdcall
*) (void *))proc_stderr_thread
, pproc
, 0,
686 (unsigned int *) &dwStderr
);
688 if (tStdout
== 0 || tStderr
== 0) {
690 pproc
->last_err
= GetLastError();
691 pproc
->lerrno
= E_SCALL
;
697 * Wait for all I/O to finish and for the child process to exit
700 while (!stdin_eof
|| !stdout_eof
|| !stderr_eof
|| !child_dead
) {
703 wait_list
[wait_count
++] = tStdin
;
706 wait_list
[wait_count
++] = tStdout
;
709 wait_list
[wait_count
++] = tStderr
;
712 wait_list
[wait_count
++] = childhand
;
715 wait_return
= WaitForMultipleObjects(wait_count
, wait_list
,
716 FALSE
, /* don't wait for all: one ready will do */
717 child_dead
? 1000 :INFINITE
); /* after the child dies, subthreads have
718 one second to collect all remaining output */
720 if (wait_return
== WAIT_FAILED
) {
721 /* map_win32_error_to_string(GetLastError());*/
722 pproc
->last_err
= GetLastError();
723 pproc
->lerrno
= E_SCALL
;
727 ready_hand
= wait_list
[wait_return
- WAIT_OBJECT_0
];
729 if (ready_hand
== tStdin
) {
730 CloseHandle((HANDLE
)pproc
->sv_stdin
[0]);
731 (HANDLE
)pproc
->sv_stdin
[0] = 0;
736 } else if (ready_hand
== tStdout
) {
738 CloseHandle((HANDLE
)pproc
->sv_stdout
[0]);
739 (HANDLE
)pproc
->sv_stdout
[0] = 0;
740 CloseHandle(tStdout
);
744 } else if (ready_hand
== tStderr
) {
746 CloseHandle((HANDLE
)pproc
->sv_stderr
[0]);
747 (HANDLE
)pproc
->sv_stderr
[0] = 0;
748 CloseHandle(tStderr
);
752 } else if (ready_hand
== childhand
) {
754 if (GetExitCodeProcess(childhand
, &pproc
->exit_code
) == FALSE
) {
755 pproc
->last_err
= GetLastError();
756 pproc
->lerrno
= E_SCALL
;
763 /* ?? Got back a handle we didn't query ?? */
765 pproc
->lerrno
= E_FAIL
;
774 CloseHandle(tStdout
);
776 CloseHandle(tStderr
);
786 * Purpose: collects output from child process and returns results
792 * Notes/Dependencies:
803 pproc
= process_wait_for_any_private();
805 pproc
= (sub_process
*)proc
;
807 /* some sort of internal error */
811 childhand
= (HANDLE
) pproc
->pid
;
814 * This function is poorly named, and could also be used just to wait
815 * for child death if you're doing your own pipe I/O. If that is
816 * the case, close the pipe handles here.
818 if (pproc
->sv_stdin
[0]) {
819 CloseHandle((HANDLE
)pproc
->sv_stdin
[0]);
820 pproc
->sv_stdin
[0] = 0;
822 if (pproc
->sv_stdout
[0]) {
823 CloseHandle((HANDLE
)pproc
->sv_stdout
[0]);
824 pproc
->sv_stdout
[0] = 0;
826 if (pproc
->sv_stderr
[0]) {
827 CloseHandle((HANDLE
)pproc
->sv_stderr
[0]);
828 pproc
->sv_stderr
[0] = 0;
832 * Wait for the child process to exit
835 wait_return
= WaitForSingleObject(childhand
, INFINITE
);
837 if (wait_return
!= WAIT_OBJECT_0
) {
838 /* map_win32_error_to_string(GetLastError());*/
839 pproc
->last_err
= GetLastError();
840 pproc
->lerrno
= E_SCALL
;
844 if (GetExitCodeProcess(childhand
, &pproc
->exit_code
) == FALSE
) {
845 pproc
->last_err
= GetLastError();
846 pproc
->lerrno
= E_SCALL
;
858 * Description: Clean up any leftover handles, etc. It is up to the
859 * caller to manage and free the input, ouput, and stderr buffers.
865 sub_process
*pproc
= (sub_process
*)proc
;
868 if (pproc
->using_pipes
) {
869 for (i
= 0; i
<= 1; i
++) {
870 if ((HANDLE
)pproc
->sv_stdin
[i
])
871 CloseHandle((HANDLE
)pproc
->sv_stdin
[i
]);
872 if ((HANDLE
)pproc
->sv_stdout
[i
])
873 CloseHandle((HANDLE
)pproc
->sv_stdout
[i
]);
874 if ((HANDLE
)pproc
->sv_stderr
[i
])
875 CloseHandle((HANDLE
)pproc
->sv_stderr
[i
]);
878 if ((HANDLE
)pproc
->pid
)
879 CloseHandle((HANDLE
)pproc
->pid
);
886 * Try to protect against WIN32 argument munging. This function takes
887 * an argv vector and outputs a 'protected' string as a return
888 * value. The return code can be safely passed to CreateProcess().
890 * The caller should free the return value.
894 static char *fix_command_line(char *args
[])
903 for (i
= 0; args
[i
]; i
++)
904 alloc_len
+= ((strlen(args
[i
]) * 2) + 1);
905 /* account for possible enclosing quotes and null termination */
908 nargp
= narg
= malloc(alloc_len
);
910 for (i
= 0; args
[i
]; i
++) {
912 TRACE(("original arg: %s\n", p
));
918 TRACE(("empty string arg: %s\n", nargp
-2));
919 } else if (strpbrk(p
, "\" \t")) {
920 /* point to end of copy buffer */
923 *q
-- = '\0'; /* ensure null terminated string */
924 *q
-- = '"'; /* terminating quote of argument */
926 /* point to end of the input string */
928 p
+= strlen(args
[i
]);
932 * Because arg is quoted, escape any backslashes
933 * that might occur at the end of the string which
934 * proceed the closing quote.
941 *q
-- = *p
--, *q
-- = '\\';
943 /* copy the string in reverse */
944 while (p
>= args
[i
]) {
945 /* copy the character */
949 * Escape any double quote found. Also escape
950 * each backslash preceding the double quote.
954 if (p
>= args
[i
] && *p
== '\\')
955 while (p
>= args
[i
] && *p
== '\\')
956 *q
-- = *p
--, *q
-- = '\\';
960 /* finish quoting arg, q now points to complete arg */
964 memmove(nargp
, q
, strlen(q
) + 1);
965 TRACE(("arg with white space or doublequotes: %s\n", nargp
));
966 nargp
+= strlen(nargp
);
968 /* just copy the argument, no protection needed */
969 strcpy(nargp
, args
[i
]);
970 TRACE(("plain arg: %s\n", nargp
));
971 nargp
+= strlen(nargp
);
974 /* separate arguments with spaces (if more args to gather) */
980 /* NULL terminate the arg list */
989 * Create a command line buffer to pass to CreateProcess
991 * Returns: the buffer or NULL for failure
992 * Shell case: sh_name a:/full/path/to/script argv[1] argv[2] ...
993 * Otherwise: argv[0] argv[1] argv[2] ...
995 * Notes/Dependencies:
996 * CreateProcess does not take an argv, so this command creates a
997 * command line for the executable.
1001 make_command_line( char *shell_name
, char *exec_path
, char **argv
)
1008 for (i
= 0; argv
[i
]; i
++);
1010 nargv
= (char **) malloc(i
* sizeof (char *));
1011 nargv
[0] = shell_name
;
1012 for (i
= 1; argv
[i
-1]; i
++)
1013 nargv
[i
] = argv
[i
-1];
1018 /* create string suitable for CreateProcess() */
1019 buf
= fix_command_line(nargv
);
1028 * Description: Given an argv and optional envp, launch the process
1029 * using the default stdin, stdout, and stderr handles.
1030 * Also, register process so that process_wait_for_any_private()
1031 * can be used via process_file_io(NULL) or
1032 * process_wait_for_any().
1036 * Notes/Dependencies:
1048 if (DuplicateHandle(GetCurrentProcess(),
1049 GetStdHandle(STD_INPUT_HANDLE
),
1050 GetCurrentProcess(),
1054 DUPLICATE_SAME_ACCESS
) == FALSE
) {
1056 "process_easy: DuplicateHandle(In) failed (e=%d)\n",
1058 return INVALID_HANDLE_VALUE
;
1060 if (DuplicateHandle(GetCurrentProcess(),
1061 GetStdHandle(STD_OUTPUT_HANDLE
),
1062 GetCurrentProcess(),
1066 DUPLICATE_SAME_ACCESS
) == FALSE
) {
1068 "process_easy: DuplicateHandle(Out) failed (e=%d)\n",
1070 return INVALID_HANDLE_VALUE
;
1072 if (DuplicateHandle(GetCurrentProcess(),
1073 GetStdHandle(STD_ERROR_HANDLE
),
1074 GetCurrentProcess(),
1078 DUPLICATE_SAME_ACCESS
) == FALSE
) {
1080 "process_easy: DuplicateHandle(Err) failed (e=%d)\n",
1082 return INVALID_HANDLE_VALUE
;
1085 hProcess
= process_init_fd(hIn
, hOut
, hErr
);
1087 if (process_begin(hProcess
, argv
, envp
, argv
[0], NULL
)) {
1088 fake_exits_pending
++;
1089 ((sub_process
*) hProcess
)->exit_code
= process_last_err(hProcess
);
1091 /* close up unused handles */
1097 process_register(hProcess
);