VirtualBox

source: kBuild/trunk/src/kmk/w32/subproc/sub_proc.c@ 3051

Last change on this file since 3051 was 3051, checked in by bird, 8 years ago

kmk,kWorker: More aggressive handling of --nice (and --priority) on windows.

  • Property svn:eol-style set to native
File size: 36.1 KB
Line 
1/* Process handling for Windows.
2Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
32006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
4This file is part of GNU Make.
5
6GNU Make is free software; you can redistribute it and/or modify it under the
7terms of the GNU General Public License as published by the Free Software
8Foundation; either version 3 of the License, or (at your option) any later
9version.
10
11GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License along with
16this program. If not, see <http://www.gnu.org/licenses/>. */
17
18#include <config.h>
19#include <stdlib.h>
20#include <stdio.h>
21#ifdef _MSC_VER
22# include <stddef.h> /* for intptr_t */
23#else
24# include <stdint.h>
25#endif
26#include <process.h> /* for msvc _beginthreadex, _endthreadex */
27#include <signal.h>
28#include <windows.h>
29#ifdef KMK
30# include <assert.h>
31# include "make.h"
32# include "kmkbuiltin.h"
33#endif
34
35
36#include "sub_proc.h"
37#include "proc.h"
38#include "w32err.h"
39#include "debug.h"
40
41static char *make_command_line(char *shell_name, char *exec_path, char **argv);
42#ifndef KMK
43extern char *xmalloc (unsigned int);
44#else
45extern void kmk_cache_exec_image(const char *); /* imagecache.c */
46#endif
47
48typedef struct sub_process_t {
49#ifdef KMK
50 enum { kRegular = 0, kSubmit, kSubProcFreed } enmType;
51 intptr_t clue;
52#endif
53 intptr_t sv_stdin[2];
54 intptr_t sv_stdout[2];
55 intptr_t sv_stderr[2];
56 int using_pipes;
57 char *inp;
58 DWORD incnt;
59 char * volatile outp;
60 volatile DWORD outcnt;
61 char * volatile errp;
62 volatile DWORD errcnt;
63 pid_t pid;
64 int exit_code;
65 int signal;
66 long last_err;
67 long lerrno;
68} sub_process;
69
70static long process_file_io_private(sub_process *pproc, BOOL fNeedToWait); /* bird */
71
72/* keep track of children so we can implement a waitpid-like routine */
73static sub_process *proc_array[MAXIMUM_WAIT_OBJECTS];
74static int proc_index = 0;
75static int fake_exits_pending = 0;
76
77#ifndef KMK /* Inefficient! */
78/*
79 * When a process has been waited for, adjust the wait state
80 * array so that we don't wait for it again
81 */
82static void
83process_adjust_wait_state(sub_process* pproc)
84{
85 int i;
86
87 if (!proc_index)
88 return;
89
90 for (i = 0; i < proc_index; i++)
91 if (proc_array[i]->pid == pproc->pid)
92 break;
93
94 if (i < proc_index) {
95 proc_index--;
96 if (i != proc_index)
97 memmove(&proc_array[i], &proc_array[i+1],
98 (proc_index-i) * sizeof(sub_process*));
99 proc_array[proc_index] = NULL;
100 }
101}
102#endif /* !KMK */
103
104/*
105 * Waits for any of the registered child processes to finish.
106 */
107static sub_process *
108process_wait_for_any_private(void)
109{
110 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
111 DWORD retval, which;
112 int i;
113
114 if (!proc_index)
115 return NULL;
116
117 /* build array of handles to wait for */
118 for (i = 0; i < proc_index; i++) {
119 handles[i] = (HANDLE) proc_array[i]->pid;
120
121 if (fake_exits_pending && proc_array[i]->exit_code)
122 break;
123 }
124
125 /* wait for someone to exit */
126 if (!fake_exits_pending) {
127#ifdef KMK
128l_wait_again:
129#endif
130 retval = WaitForMultipleObjects(proc_index, handles, FALSE, INFINITE);
131 which = retval - WAIT_OBJECT_0;
132 } else {
133 fake_exits_pending--;
134 retval = !WAIT_FAILED;
135 which = i;
136 }
137
138 /* return pointer to process */
139 if (retval != WAIT_FAILED) {
140 sub_process* pproc = proc_array[which];
141#ifdef KMK
142 if (pproc->enmType == kSubmit) {
143 /* Try get the result from kSubmit.c. This may not succeed if the whole
144 result hasn't arrived yet, in which we just restart the wait. */
145 if (kSubmitSubProcGetResult(pproc->clue, &pproc->exit_code, &pproc->signal) != 0) {
146 goto l_wait_again;
147 }
148 }
149#endif
150#ifndef KMK /* Inefficient! */
151 process_adjust_wait_state(pproc);
152#else
153 proc_index--;
154 if ((int)which < proc_index)
155 proc_array[which] = proc_array[proc_index];
156 proc_array[proc_index] = NULL;
157#endif
158 return pproc;
159 } else
160 return NULL;
161}
162
163/*
164 * Terminate a process.
165 */
166BOOL
167 process_kill(HANDLE proc, int signal)
168{
169 sub_process* pproc = (sub_process*) proc;
170 pproc->signal = signal;
171#ifdef KMK
172 if (pproc->enmType == kRegular) {
173#endif
174 return (TerminateProcess((HANDLE) pproc->pid, signal));
175#ifdef KMK
176 } else if (pproc->enmType == kSubmit) {
177 return kSubmitSubProcKill(pproc->clue, signal) == 0;
178 }
179 assert(0);
180 return FALSE;
181#endif
182}
183
184/*
185 * Use this function to register processes you wish to wait for by
186 * calling process_file_io(NULL) or process_wait_any(). This must be done
187 * because it is possible for callers of this library to reuse the same
188 * handle for multiple processes launches :-(
189 */
190void
191process_register(HANDLE proc)
192{
193#ifdef KMK
194 assert(((sub_process *)proc)->enmType == kRegular);
195#endif
196 if (proc_index < MAXIMUM_WAIT_OBJECTS)
197 proc_array[proc_index++] = (sub_process *) proc;
198}
199
200#ifdef KMK
201
202/**
203 * Interface used by kmkbuiltin/kSubmit.c to register stuff going down in a
204 * worker process.
205 *
206 * @returns 0 on success, -1 if there are too many sub-processes already.
207 * @param hEvent The event semaphore to wait on.
208 * @param clue The clue to base.
209 * @param pPid Where to return the pid that job.c expects.
210 */
211int
212process_kmk_register_submit(HANDLE hEvent, intptr_t clue, pid_t *pPid)
213{
214 if (proc_index < MAXIMUM_WAIT_OBJECTS) {
215 sub_process *pSubProc = (sub_process *)xcalloc(sizeof(*pSubProc));
216 pSubProc->enmType = kSubmit;
217 pSubProc->clue = clue;
218 pSubProc->pid = (intptr_t)hEvent;
219
220 proc_array[proc_index++] = pSubProc;
221 *pPid = (intptr_t)pSubProc;
222 return 0;
223 }
224 return -1;
225}
226
227/**
228 * Interface used by kmkbuiltin/kRedirect.c to register a spawned process.
229 *
230 * @returns 0 on success, -1 if there are too many sub-processes already.
231 * @param hProcess The process handle.
232 * @param pPid Where to return the pid that job.c expects.
233 */
234int
235process_kmk_register_redirect(HANDLE hProcess, pid_t *pPid)
236{
237 if (proc_index < MAXIMUM_WAIT_OBJECTS) {
238 sub_process *pSubProc = (sub_process *)xcalloc(sizeof(*pSubProc));
239 pSubProc->enmType = kRegular;
240 pSubProc->pid = (intptr_t)hProcess;
241
242 proc_array[proc_index++] = pSubProc;
243 *pPid = (intptr_t)pSubProc;
244 return 0;
245 }
246 return -1;
247}
248
249#endif /* KMK */
250
251/*
252 * Return the number of processes that we are still waiting for.
253 */
254int
255process_used_slots(void)
256{
257 return proc_index;
258}
259
260/*
261 * Public function which works kind of like waitpid(). Wait for any
262 * of the children to die and return results. To call this function,
263 * you must do 1 of things:
264 *
265 * x = process_easy(...);
266 *
267 * or
268 *
269 * x = process_init_fd();
270 * process_register(x);
271 *
272 * or
273 *
274 * x = process_init();
275 * process_register(x);
276 *
277 * You must NOT then call process_pipe_io() because this function is
278 * not capable of handling automatic notification of any child
279 * death.
280 */
281
282HANDLE
283process_wait_for_any(void)
284{
285 sub_process* pproc = process_wait_for_any_private();
286
287 if (!pproc)
288 return NULL;
289 else {
290 /*
291 * Ouch! can't tell caller if this fails directly. Caller
292 * will have to use process_last_err()
293 */
294#ifdef KMK
295 /* Invalidate negative directory cache entries now that a
296 job has completed and possibly created new files that
297 was missing earlier. */
298 dir_cache_invalid_after_job ();
299
300 if (pproc->enmType == kRegular) {
301 (void)process_file_io_private(pproc, FALSE);
302 }
303#else
304 (void) process_file_io(pproc);
305#endif
306 return ((HANDLE) pproc);
307 }
308}
309
310long
311process_signal(HANDLE proc)
312{
313 if (proc == INVALID_HANDLE_VALUE) return 0;
314 return (((sub_process *)proc)->signal);
315}
316
317long
318process_last_err(HANDLE proc)
319{
320 if (proc == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
321 return (((sub_process *)proc)->last_err);
322}
323
324long
325process_exit_code(HANDLE proc)
326{
327 if (proc == INVALID_HANDLE_VALUE) return EXIT_FAILURE;
328 return (((sub_process *)proc)->exit_code);
329}
330
331/*
3322006-02:
333All the following functions are currently unused.
334All of them would crash gmake if called with argument INVALID_HANDLE_VALUE.
335Hence whoever wants to use one of this functions must invent and implement
336a reasonable error handling for this function.
337
338char *
339process_outbuf(HANDLE proc)
340{
341 return (((sub_process *)proc)->outp);
342}
343
344char *
345process_errbuf(HANDLE proc)
346{
347 return (((sub_process *)proc)->errp);
348}
349
350int
351process_outcnt(HANDLE proc)
352{
353 return (((sub_process *)proc)->outcnt);
354}
355
356int
357process_errcnt(HANDLE proc)
358{
359 return (((sub_process *)proc)->errcnt);
360}
361
362void
363process_pipes(HANDLE proc, int pipes[3])
364{
365 pipes[0] = ((sub_process *)proc)->sv_stdin[0];
366 pipes[1] = ((sub_process *)proc)->sv_stdout[0];
367 pipes[2] = ((sub_process *)proc)->sv_stderr[0];
368 return;
369}
370*/
371
372 HANDLE
373process_init()
374{
375 sub_process *pproc;
376 /*
377 * open file descriptors for attaching stdin/stdout/sterr
378 */
379 HANDLE stdin_pipes[2];
380 HANDLE stdout_pipes[2];
381 HANDLE stderr_pipes[2];
382 SECURITY_ATTRIBUTES inherit;
383 BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
384
385 pproc = malloc(sizeof(*pproc));
386 memset(pproc, 0, sizeof(*pproc));
387
388 /* We can't use NULL for lpSecurityDescriptor because that
389 uses the default security descriptor of the calling process.
390 Instead we use a security descriptor with no DACL. This
391 allows nonrestricted access to the associated objects. */
392
393 if (!InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR)(&sd),
394 SECURITY_DESCRIPTOR_REVISION)) {
395 pproc->last_err = GetLastError();
396 pproc->lerrno = E_SCALL;
397 return((HANDLE)pproc);
398 }
399
400 inherit.nLength = sizeof(inherit);
401 inherit.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)(&sd);
402 inherit.bInheritHandle = TRUE;
403
404 // By convention, parent gets pipe[0], and child gets pipe[1]
405 // This means the READ side of stdin pipe goes into pipe[1]
406 // and the WRITE side of the stdout and stderr pipes go into pipe[1]
407 if (CreatePipe( &stdin_pipes[1], &stdin_pipes[0], &inherit, 0) == FALSE ||
408 CreatePipe( &stdout_pipes[0], &stdout_pipes[1], &inherit, 0) == FALSE ||
409 CreatePipe( &stderr_pipes[0], &stderr_pipes[1], &inherit, 0) == FALSE) {
410
411 pproc->last_err = GetLastError();
412 pproc->lerrno = E_SCALL;
413 return((HANDLE)pproc);
414 }
415
416 //
417 // Mark the parent sides of the pipes as non-inheritable
418 //
419 if (SetHandleInformation(stdin_pipes[0],
420 HANDLE_FLAG_INHERIT, 0) == FALSE ||
421 SetHandleInformation(stdout_pipes[0],
422 HANDLE_FLAG_INHERIT, 0) == FALSE ||
423 SetHandleInformation(stderr_pipes[0],
424 HANDLE_FLAG_INHERIT, 0) == FALSE) {
425
426 pproc->last_err = GetLastError();
427 pproc->lerrno = E_SCALL;
428 return((HANDLE)pproc);
429 }
430 pproc->sv_stdin[0] = (intptr_t) stdin_pipes[0];
431 pproc->sv_stdin[1] = (intptr_t) stdin_pipes[1];
432 pproc->sv_stdout[0] = (intptr_t) stdout_pipes[0];
433 pproc->sv_stdout[1] = (intptr_t) stdout_pipes[1];
434 pproc->sv_stderr[0] = (intptr_t) stderr_pipes[0];
435 pproc->sv_stderr[1] = (intptr_t) stderr_pipes[1];
436
437 pproc->using_pipes = 1;
438
439 pproc->lerrno = 0;
440
441 return((HANDLE)pproc);
442}
443
444
445 HANDLE
446process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh)
447{
448 sub_process *pproc;
449
450 pproc = malloc(sizeof(*pproc));
451 memset(pproc, 0, sizeof(*pproc));
452
453 /*
454 * Just pass the provided file handles to the 'child side' of the
455 * pipe, bypassing pipes altogether.
456 */
457 pproc->sv_stdin[1] = (intptr_t) stdinh;
458 pproc->sv_stdout[1] = (intptr_t) stdouth;
459 pproc->sv_stderr[1] = (intptr_t) stderrh;
460
461 pproc->last_err = pproc->lerrno = 0;
462
463 return((HANDLE)pproc);
464}
465
466
467static HANDLE
468find_file(const char *exec_path, const char *path_var,
469 char *full_fname, DWORD full_len)
470{
471 HANDLE exec_handle;
472 char *fname;
473 char *ext;
474 DWORD req_len;
475 int i;
476 static const char *extensions[] =
477 /* Should .com come before no-extension case? */
478 { ".exe", ".cmd", ".bat", "", ".com", NULL };
479
480 fname = xmalloc(strlen(exec_path) + 5);
481 strcpy(fname, exec_path);
482 ext = fname + strlen(fname);
483
484 for (i = 0; extensions[i]; i++) {
485 strcpy(ext, extensions[i]);
486 if (((req_len = SearchPath (path_var, fname, NULL, full_len,
487 full_fname, NULL)) > 0
488 /* For compatibility with previous code, which
489 used OpenFile, and with Windows operation in
490 general, also look in various default
491 locations, such as Windows directory and
492 Windows System directory. Warning: this also
493 searches PATH in the Make's environment, which
494 might not be what the Makefile wants, but it
495 seems to be OK as a fallback, after the
496 previous SearchPath failed to find on child's
497 PATH. */
498 || (req_len = SearchPath (NULL, fname, NULL, full_len,
499 full_fname, NULL)) > 0)
500 && req_len <= full_len
501 && (exec_handle =
502 CreateFile(full_fname,
503 GENERIC_READ,
504 FILE_SHARE_READ | FILE_SHARE_WRITE,
505 NULL,
506 OPEN_EXISTING,
507 FILE_ATTRIBUTE_NORMAL,
508 NULL)) != INVALID_HANDLE_VALUE) {
509 free(fname);
510 return(exec_handle);
511 }
512 }
513
514 free(fname);
515 return INVALID_HANDLE_VALUE;
516}
517
518
519/*
520 * Description: Create the child process to be helped
521 *
522 * Returns: success <=> 0
523 *
524 * Notes/Dependencies:
525 */
526long
527process_begin(
528 HANDLE proc,
529 char **argv,
530 char **envp,
531 char *exec_path,
532 char *as_user)
533{
534 sub_process *pproc = (sub_process *)proc;
535 char *shell_name = 0;
536 int file_not_found=0;
537 HANDLE exec_handle;
538 char exec_fname[MAX_PATH];
539 const char *path_var = NULL;
540 char **ep;
541 char buf[256];
542 DWORD bytes_returned;
543 DWORD flags;
544 char *command_line;
545 STARTUPINFO startInfo;
546 PROCESS_INFORMATION procInfo;
547 char *envblk=NULL;
548#ifdef KMK
549 size_t exec_path_len;
550 extern int process_priority;
551
552 assert (pproc->enmType == kRegular);
553#endif
554
555
556 /*
557 * Shell script detection... if the exec_path starts with #! then
558 * we want to exec shell-script-name exec-path, not just exec-path
559 * NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl. We do not
560 * hard-code the path to the shell or perl or whatever: Instead, we
561 * assume it's in the path somewhere (generally, the NT tools
562 * bin directory)
563 */
564
565#ifdef KMK
566 /* kmk performance: Don't bother looking for shell scripts in .exe files. */
567 exec_path_len = strlen(exec_path);
568 if (exec_path_len > 4
569 && exec_path[exec_path_len - 4] == '.'
570 && !stricmp(exec_path + exec_path_len - 3, "exe")) {
571 exec_handle = INVALID_HANDLE_VALUE;
572 exec_fname[0] = '\0';
573 }
574 else {
575#endif /* KMK */
576 /* Use the Makefile's value of PATH to look for the program to
577 execute, because it could be different from Make's PATH
578 (e.g., if the target sets its own value. */
579 if (envp)
580 for (ep = envp; *ep; ep++) {
581 if (strncmp (*ep, "PATH=", 5) == 0
582 || strncmp (*ep, "Path=", 5) == 0) {
583 path_var = *ep + 5;
584 break;
585 }
586 }
587 exec_handle = find_file(exec_path, path_var,
588 exec_fname, sizeof(exec_fname));
589#ifdef KMK
590 }
591#endif
592
593 /*
594 * If we couldn't open the file, just assume that Windows will be
595 * somehow able to find and execute it.
596 */
597 if (exec_handle == INVALID_HANDLE_VALUE) {
598 file_not_found++;
599 }
600 else {
601 /* Attempt to read the first line of the file */
602 if (ReadFile( exec_handle,
603 buf, sizeof(buf) - 1, /* leave room for trailing NULL */
604 &bytes_returned, 0) == FALSE || bytes_returned < 2) {
605
606 pproc->last_err = GetLastError();
607 pproc->lerrno = E_IO;
608 CloseHandle(exec_handle);
609 return(-1);
610 }
611 if (buf[0] == '#' && buf[1] == '!') {
612 /*
613 * This is a shell script... Change the command line from
614 * exec_path args to shell_name exec_path args
615 */
616 char *p;
617
618 /* Make sure buf is NULL terminated */
619 buf[bytes_returned] = 0;
620 /*
621 * Depending on the file system type, etc. the first line
622 * of the shell script may end with newline or newline-carriage-return
623 * Whatever it ends with, cut it off.
624 */
625 p= strchr(buf, '\n');
626 if (p)
627 *p = 0;
628 p = strchr(buf, '\r');
629 if (p)
630 *p = 0;
631
632 /*
633 * Find base name of shell
634 */
635 shell_name = strrchr( buf, '/');
636 if (shell_name) {
637 shell_name++;
638 } else {
639 shell_name = &buf[2];/* skipping "#!" */
640 }
641
642 }
643 CloseHandle(exec_handle);
644 }
645
646 flags = 0;
647
648 if (file_not_found)
649 command_line = make_command_line( shell_name, exec_path, argv);
650 else
651 command_line = make_command_line( shell_name, exec_fname, argv);
652
653 if ( command_line == NULL ) {
654 pproc->last_err = 0;
655 pproc->lerrno = E_NO_MEM;
656 return(-1);
657 }
658
659 if (envp) {
660 if (arr2envblk(envp, &envblk) ==FALSE) {
661 pproc->last_err = 0;
662 pproc->lerrno = E_NO_MEM;
663 free( command_line );
664 return(-1);
665 }
666 }
667
668 if ((shell_name) || (file_not_found)) {
669 exec_path = 0; /* Search for the program in %Path% */
670 } else {
671 exec_path = exec_fname;
672 }
673
674 /*
675 * Set up inherited stdin, stdout, stderr for child
676 */
677 GetStartupInfo(&startInfo);
678#ifndef KMK
679 startInfo.dwFlags = STARTF_USESTDHANDLES;
680#endif
681 startInfo.lpReserved = 0;
682 startInfo.cbReserved2 = 0;
683 startInfo.lpReserved2 = 0;
684 startInfo.lpTitle = shell_name ? shell_name : exec_path;
685#ifndef KMK
686 startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1];
687 startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1];
688 startInfo.hStdError = (HANDLE)pproc->sv_stderr[1];
689#else
690 if ( pproc->sv_stdin[1]
691 || pproc->sv_stdout[1]
692 || pproc->sv_stderr[1]) {
693 startInfo.dwFlags = STARTF_USESTDHANDLES;
694 startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1];
695 startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1];
696 startInfo.hStdError = (HANDLE)pproc->sv_stderr[1];
697 } else {
698 startInfo.dwFlags = 0;
699 startInfo.hStdInput = 0;
700 startInfo.hStdOutput = 0;
701 startInfo.hStdError = 0;
702 }
703#endif
704
705 if (as_user) {
706 if (envblk) free(envblk);
707 return -1;
708 } else {
709 DB (DB_JOBS, ("CreateProcess(%s,%s,...)\n",
710 exec_path ? exec_path : "NULL",
711 command_line ? command_line : "NULL"));
712#ifdef KMK
713 if (exec_fname[0])
714 kmk_cache_exec_image(exec_fname);
715 else if (exec_path)
716 kmk_cache_exec_image(exec_path);
717 else if (argv[0])
718 kmk_cache_exec_image(argv[0]);
719
720 switch (process_priority) {
721 case 1: flags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break;
722 case 2: flags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break;
723 case 3: flags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break;
724 case 4: flags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break;
725 case 5: flags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break;
726 }
727#endif
728 if (CreateProcess(
729 exec_path,
730 command_line,
731 NULL,
732 0, /* default security attributes for thread */
733 TRUE, /* inherit handles (e.g. helper pipes, oserv socket) */
734 flags,
735 envblk,
736 0, /* default starting directory */
737 &startInfo,
738 &procInfo) == FALSE) {
739
740 pproc->last_err = GetLastError();
741 pproc->lerrno = E_FORK;
742#ifdef KMK
743 if (pproc->last_err == ERROR_FILE_NOT_FOUND)
744 pproc->exit_code = 127; /* see execve failure in job.c. */
745#endif
746 fprintf(stderr, "process_begin: CreateProcess(%s, %s, ...) failed.\n",
747 exec_path ? exec_path : "NULL", command_line);
748 if (envblk) free(envblk);
749 free( command_line );
750 return(-1);
751 }
752#ifdef KMK
753 switch (process_priority) {
754 case 1: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_IDLE); break;
755 case 2: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break;
756 case 3: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_NORMAL); break;
757 case 4: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_HIGHEST); break;
758 case 5: SetThreadPriority(procInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break;
759 }
760 ResumeThread(procInfo.hThread);
761#endif
762 }
763
764 pproc->pid = (pid_t)procInfo.hProcess;
765 /* Close the thread handle -- we'll just watch the process */
766 CloseHandle(procInfo.hThread);
767
768 /* Close the halves of the pipes we don't need */
769#ifndef KMK
770 CloseHandle((HANDLE)pproc->sv_stdin[1]);
771 CloseHandle((HANDLE)pproc->sv_stdout[1]);
772 CloseHandle((HANDLE)pproc->sv_stderr[1]);
773 pproc->sv_stdin[1] = 0;
774 pproc->sv_stdout[1] = 0;
775 pproc->sv_stderr[1] = 0;
776#else
777 if ((HANDLE)pproc->sv_stdin[1]) {
778 CloseHandle((HANDLE)pproc->sv_stdin[1]);
779 pproc->sv_stdin[1] = 0;
780 }
781 if ((HANDLE)pproc->sv_stdout[1]) {
782 CloseHandle((HANDLE)pproc->sv_stdout[1]);
783 pproc->sv_stdout[1] = 0;
784 }
785 if ((HANDLE)pproc->sv_stderr[1]) {
786 CloseHandle((HANDLE)pproc->sv_stderr[1]);
787 pproc->sv_stderr[1] = 0;
788 }
789#endif
790
791 free( command_line );
792 if (envblk) free(envblk);
793 pproc->lerrno=0;
794 return 0;
795}
796
797
798
799static DWORD
800proc_stdin_thread(sub_process *pproc)
801{
802 DWORD in_done;
803 for (;;) {
804 if (WriteFile( (HANDLE) pproc->sv_stdin[0], pproc->inp, pproc->incnt,
805 &in_done, NULL) == FALSE)
806 _endthreadex(0);
807 // This if should never be true for anonymous pipes, but gives
808 // us a chance to change I/O mechanisms later
809 if (in_done < pproc->incnt) {
810 pproc->incnt -= in_done;
811 pproc->inp += in_done;
812 } else {
813 _endthreadex(0);
814 }
815 }
816 return 0; // for compiler warnings only.. not reached
817}
818
819static DWORD
820proc_stdout_thread(sub_process *pproc)
821{
822 DWORD bufsize = 1024;
823 char c;
824 DWORD nread;
825 pproc->outp = malloc(bufsize);
826 if (pproc->outp == NULL)
827 _endthreadex(0);
828 pproc->outcnt = 0;
829
830 for (;;) {
831 if (ReadFile( (HANDLE)pproc->sv_stdout[0], &c, 1, &nread, NULL)
832 == FALSE) {
833/* map_windows32_error_to_string(GetLastError());*/
834 _endthreadex(0);
835 }
836 if (nread == 0)
837 _endthreadex(0);
838 if (pproc->outcnt + nread > bufsize) {
839 bufsize += nread + 512;
840 pproc->outp = realloc(pproc->outp, bufsize);
841 if (pproc->outp == NULL) {
842 pproc->outcnt = 0;
843 _endthreadex(0);
844 }
845 }
846 pproc->outp[pproc->outcnt++] = c;
847 }
848 return 0;
849}
850
851static DWORD
852proc_stderr_thread(sub_process *pproc)
853{
854 DWORD bufsize = 1024;
855 char c;
856 DWORD nread;
857 pproc->errp = malloc(bufsize);
858 if (pproc->errp == NULL)
859 _endthreadex(0);
860 pproc->errcnt = 0;
861
862 for (;;) {
863 if (ReadFile( (HANDLE)pproc->sv_stderr[0], &c, 1, &nread, NULL) == FALSE) {
864 map_windows32_error_to_string(GetLastError());
865 _endthreadex(0);
866 }
867 if (nread == 0)
868 _endthreadex(0);
869 if (pproc->errcnt + nread > bufsize) {
870 bufsize += nread + 512;
871 pproc->errp = realloc(pproc->errp, bufsize);
872 if (pproc->errp == NULL) {
873 pproc->errcnt = 0;
874 _endthreadex(0);
875 }
876 }
877 pproc->errp[pproc->errcnt++] = c;
878 }
879 return 0;
880}
881
882
883/*
884 * Purpose: collects output from child process and returns results
885 *
886 * Description:
887 *
888 * Returns:
889 *
890 * Notes/Dependencies:
891 */
892 long
893process_pipe_io(
894 HANDLE proc,
895 char *stdin_data,
896 int stdin_data_len)
897{
898 sub_process *pproc = (sub_process *)proc;
899 bool_t stdin_eof = FALSE, stdout_eof = FALSE, stderr_eof = FALSE;
900 HANDLE childhand = (HANDLE) pproc->pid;
901 HANDLE tStdin = NULL, tStdout = NULL, tStderr = NULL;
902 unsigned int dwStdin, dwStdout, dwStderr;
903 HANDLE wait_list[4];
904 DWORD wait_count;
905 DWORD wait_return;
906 HANDLE ready_hand;
907 bool_t child_dead = FALSE;
908 BOOL GetExitCodeResult;
909#ifdef KMK
910 assert (pproc->enmType == kRegular);
911#endif
912
913 /*
914 * Create stdin thread, if needed
915 */
916 pproc->inp = stdin_data;
917 pproc->incnt = stdin_data_len;
918 if (!pproc->inp) {
919 stdin_eof = TRUE;
920 CloseHandle((HANDLE)pproc->sv_stdin[0]);
921 pproc->sv_stdin[0] = 0;
922 } else {
923 tStdin = (HANDLE) _beginthreadex( 0, 1024,
924 (unsigned (__stdcall *) (void *))proc_stdin_thread,
925 pproc, 0, &dwStdin);
926 if (tStdin == 0) {
927 pproc->last_err = GetLastError();
928 pproc->lerrno = E_SCALL;
929 goto done;
930 }
931 }
932
933 /*
934 * Assume child will produce stdout and stderr
935 */
936 tStdout = (HANDLE) _beginthreadex( 0, 1024,
937 (unsigned (__stdcall *) (void *))proc_stdout_thread, pproc, 0,
938 &dwStdout);
939 tStderr = (HANDLE) _beginthreadex( 0, 1024,
940 (unsigned (__stdcall *) (void *))proc_stderr_thread, pproc, 0,
941 &dwStderr);
942
943 if (tStdout == 0 || tStderr == 0) {
944
945 pproc->last_err = GetLastError();
946 pproc->lerrno = E_SCALL;
947 goto done;
948 }
949
950
951 /*
952 * Wait for all I/O to finish and for the child process to exit
953 */
954
955 while (!stdin_eof || !stdout_eof || !stderr_eof || !child_dead) {
956 wait_count = 0;
957 if (!stdin_eof) {
958 wait_list[wait_count++] = tStdin;
959 }
960 if (!stdout_eof) {
961 wait_list[wait_count++] = tStdout;
962 }
963 if (!stderr_eof) {
964 wait_list[wait_count++] = tStderr;
965 }
966 if (!child_dead) {
967 wait_list[wait_count++] = childhand;
968 }
969
970 wait_return = WaitForMultipleObjects(wait_count, wait_list,
971 FALSE, /* don't wait for all: one ready will do */
972 child_dead? 1000 :INFINITE); /* after the child dies, subthreads have
973 one second to collect all remaining output */
974
975 if (wait_return == WAIT_FAILED) {
976/* map_windows32_error_to_string(GetLastError());*/
977 pproc->last_err = GetLastError();
978 pproc->lerrno = E_SCALL;
979 goto done;
980 }
981
982 ready_hand = wait_list[wait_return - WAIT_OBJECT_0];
983
984 if (ready_hand == tStdin) {
985 CloseHandle((HANDLE)pproc->sv_stdin[0]);
986 pproc->sv_stdin[0] = 0;
987 CloseHandle(tStdin);
988 tStdin = 0;
989 stdin_eof = TRUE;
990
991 } else if (ready_hand == tStdout) {
992
993 CloseHandle((HANDLE)pproc->sv_stdout[0]);
994 pproc->sv_stdout[0] = 0;
995 CloseHandle(tStdout);
996 tStdout = 0;
997 stdout_eof = TRUE;
998
999 } else if (ready_hand == tStderr) {
1000
1001 CloseHandle((HANDLE)pproc->sv_stderr[0]);
1002 pproc->sv_stderr[0] = 0;
1003 CloseHandle(tStderr);
1004 tStderr = 0;
1005 stderr_eof = TRUE;
1006
1007 } else if (ready_hand == childhand) {
1008
1009 DWORD ierr;
1010 GetExitCodeResult = GetExitCodeProcess(childhand, &ierr);
1011 if (ierr == CONTROL_C_EXIT) {
1012 pproc->signal = SIGINT;
1013 } else {
1014 pproc->exit_code = ierr;
1015 }
1016 if (GetExitCodeResult == FALSE) {
1017 pproc->last_err = GetLastError();
1018 pproc->lerrno = E_SCALL;
1019 goto done;
1020 }
1021 child_dead = TRUE;
1022
1023 } else {
1024
1025 /* ?? Got back a handle we didn't query ?? */
1026 pproc->last_err = 0;
1027 pproc->lerrno = E_FAIL;
1028 goto done;
1029 }
1030 }
1031
1032 done:
1033 if (tStdin != 0)
1034 CloseHandle(tStdin);
1035 if (tStdout != 0)
1036 CloseHandle(tStdout);
1037 if (tStderr != 0)
1038 CloseHandle(tStderr);
1039
1040 if (pproc->lerrno)
1041 return(-1);
1042 else
1043 return(0);
1044
1045}
1046
1047#ifndef KMK /* unused */
1048/*
1049 * Purpose: collects output from child process and returns results
1050 *
1051 * Description:
1052 *
1053 * Returns:
1054 *
1055 * Notes/Dependencies:
1056 */
1057 long
1058process_file_io(
1059 HANDLE proc)
1060{
1061 sub_process *pproc;
1062 if (proc == NULL)
1063 pproc = process_wait_for_any_private();
1064 else
1065 pproc = (sub_process *)proc;
1066
1067 /* some sort of internal error */
1068 if (!pproc)
1069 return -1;
1070
1071 return process_file_io_private(proc, TRUE);
1072}
1073#endif /* !KMK - unused */
1074
1075/* private function, avoid some kernel calls. (bird) */
1076static long
1077process_file_io_private(
1078 sub_process *pproc,
1079 BOOL fNeedToWait)
1080{
1081 HANDLE childhand;
1082 DWORD wait_return;
1083 BOOL GetExitCodeResult;
1084 DWORD ierr;
1085
1086 childhand = (HANDLE) pproc->pid;
1087
1088 /*
1089 * This function is poorly named, and could also be used just to wait
1090 * for child death if you're doing your own pipe I/O. If that is
1091 * the case, close the pipe handles here.
1092 */
1093 if (pproc->sv_stdin[0]) {
1094 CloseHandle((HANDLE)pproc->sv_stdin[0]);
1095 pproc->sv_stdin[0] = 0;
1096 }
1097 if (pproc->sv_stdout[0]) {
1098 CloseHandle((HANDLE)pproc->sv_stdout[0]);
1099 pproc->sv_stdout[0] = 0;
1100 }
1101 if (pproc->sv_stderr[0]) {
1102 CloseHandle((HANDLE)pproc->sv_stderr[0]);
1103 pproc->sv_stderr[0] = 0;
1104 }
1105
1106#ifdef KMK
1107 if (childhand == NULL || childhand == INVALID_HANDLE_VALUE) {
1108 goto done2;
1109 }
1110#endif
1111
1112 /*
1113 * Wait for the child process to exit it we didn't do that already.
1114 */
1115 if (fNeedToWait) {
1116 wait_return = WaitForSingleObject(childhand, INFINITE);
1117 if (wait_return != WAIT_OBJECT_0) {
1118/* map_windows32_error_to_string(GetLastError());*/
1119 pproc->last_err = GetLastError();
1120 pproc->lerrno = E_SCALL;
1121 goto done2;
1122 }
1123 }
1124
1125 GetExitCodeResult = GetExitCodeProcess(childhand, &ierr);
1126 if (ierr == CONTROL_C_EXIT) {
1127 pproc->signal = SIGINT;
1128 } else {
1129 pproc->exit_code = ierr;
1130 }
1131 if (GetExitCodeResult == FALSE) {
1132 pproc->last_err = GetLastError();
1133 pproc->lerrno = E_SCALL;
1134 }
1135
1136done2:
1137 if (pproc->lerrno)
1138 return(-1);
1139 else
1140 return(0);
1141
1142}
1143
1144/*
1145 * Description: Clean up any leftover handles, etc. It is up to the
1146 * caller to manage and free the input, ouput, and stderr buffers.
1147 */
1148 void
1149process_cleanup(
1150 HANDLE proc)
1151{
1152 sub_process *pproc = (sub_process *)proc;
1153 int i;
1154
1155#ifdef KMK
1156 if (pproc->enmType == kRegular) {
1157#endif
1158
1159 if (pproc->using_pipes) {
1160 for (i= 0; i <= 1; i++) {
1161 if ((HANDLE)pproc->sv_stdin[i])
1162 CloseHandle((HANDLE)pproc->sv_stdin[i]);
1163 if ((HANDLE)pproc->sv_stdout[i])
1164 CloseHandle((HANDLE)pproc->sv_stdout[i]);
1165 if ((HANDLE)pproc->sv_stderr[i])
1166 CloseHandle((HANDLE)pproc->sv_stderr[i]);
1167 }
1168 }
1169 if ((HANDLE)pproc->pid)
1170 CloseHandle((HANDLE)pproc->pid);
1171#ifdef KMK
1172 } else if (pproc->enmType == kSubmit) {
1173 kSubmitSubProcCleanup(pproc->clue);
1174 } else {
1175 assert(0);
1176 return;
1177 }
1178 pproc->enmType = kSubProcFreed;
1179#endif
1180
1181 free(pproc);
1182}
1183
1184
1185/*
1186 * Description:
1187 * Create a command line buffer to pass to CreateProcess
1188 *
1189 * Returns: the buffer or NULL for failure
1190 * Shell case: sh_name a:/full/path/to/script argv[1] argv[2] ...
1191 * Otherwise: argv[0] argv[1] argv[2] ...
1192 *
1193 * Notes/Dependencies:
1194 * CreateProcess does not take an argv, so this command creates a
1195 * command line for the executable.
1196 */
1197
1198static char *
1199make_command_line( char *shell_name, char *full_exec_path, char **argv)
1200{
1201 int argc = 0;
1202 char** argvi;
1203 int* enclose_in_quotes = NULL;
1204 int* enclose_in_quotes_i;
1205 unsigned int bytes_required = 0;
1206 char* command_line;
1207 char* command_line_i;
1208 int cygwin_mode = 0; /* HAVE_CYGWIN_SHELL */
1209 int have_sh = 0; /* HAVE_CYGWIN_SHELL */
1210#undef HAVE_CYGWIN_SHELL
1211#ifdef HAVE_CYGWIN_SHELL
1212 have_sh = (shell_name != NULL || strstr(full_exec_path, "sh.exe"));
1213 cygwin_mode = 1;
1214#endif
1215
1216 if (shell_name && full_exec_path) {
1217 bytes_required
1218 = strlen(shell_name) + 1 + strlen(full_exec_path);
1219 /*
1220 * Skip argv[0] if any, when shell_name is given.
1221 */
1222 if (*argv) argv++;
1223 /*
1224 * Add one for the intervening space.
1225 */
1226 if (*argv) bytes_required++;
1227 }
1228
1229 argvi = argv;
1230 while (*(argvi++)) argc++;
1231
1232 if (argc) {
1233 enclose_in_quotes = (int*) calloc(1, argc * sizeof(int));
1234
1235 if (!enclose_in_quotes) {
1236 return NULL;
1237 }
1238 }
1239
1240 /* We have to make one pass through each argv[i] to see if we need
1241 * to enclose it in ", so we might as well figure out how much
1242 * memory we'll need on the same pass.
1243 */
1244
1245 argvi = argv;
1246 enclose_in_quotes_i = enclose_in_quotes;
1247 while(*argvi) {
1248 char* p = *argvi;
1249 unsigned int backslash_count = 0;
1250
1251 /*
1252 * We have to enclose empty arguments in ".
1253 */
1254 if (!(*p)) *enclose_in_quotes_i = 1;
1255
1256 while(*p) {
1257 switch (*p) {
1258 case '\"':
1259 /*
1260 * We have to insert a backslash for each "
1261 * and each \ that precedes the ".
1262 */
1263 bytes_required += (backslash_count + 1);
1264 backslash_count = 0;
1265 break;
1266
1267#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
1268 case '\\':
1269 backslash_count++;
1270 break;
1271#endif
1272 /*
1273 * At one time we set *enclose_in_quotes_i for '*' or '?' to suppress
1274 * wildcard expansion in programs linked with MSVC's SETARGV.OBJ so
1275 * that argv in always equals argv out. This was removed. Say you have
1276 * such a program named glob.exe. You enter
1277 * glob '*'
1278 * at the sh command prompt. Obviously the intent is to make glob do the
1279 * wildcarding instead of sh. If we set *enclose_in_quotes_i for '*' or '?',
1280 * then the command line that glob would see would be
1281 * glob "*"
1282 * and the _setargv in SETARGV.OBJ would _not_ expand the *.
1283 */
1284 case ' ':
1285 case '\t':
1286 *enclose_in_quotes_i = 1;
1287 /* fall through */
1288
1289 default:
1290 backslash_count = 0;
1291 break;
1292 }
1293
1294 /*
1295 * Add one for each character in argv[i].
1296 */
1297 bytes_required++;
1298
1299 p++;
1300 }
1301
1302 if (*enclose_in_quotes_i) {
1303 /*
1304 * Add one for each enclosing ",
1305 * and one for each \ that precedes the
1306 * closing ".
1307 */
1308 bytes_required += (backslash_count + 2);
1309 }
1310
1311 /*
1312 * Add one for the intervening space.
1313 */
1314 if (*(++argvi)) bytes_required++;
1315 enclose_in_quotes_i++;
1316 }
1317
1318 /*
1319 * Add one for the terminating NULL.
1320 */
1321 bytes_required++;
1322#ifdef KMK /* for the space before the final " in case we need it. */
1323 bytes_required++;
1324#endif
1325
1326 command_line = (char*) malloc(bytes_required);
1327
1328 if (!command_line) {
1329 if (enclose_in_quotes) free(enclose_in_quotes);
1330 return NULL;
1331 }
1332
1333 command_line_i = command_line;
1334
1335 if (shell_name && full_exec_path) {
1336 while(*shell_name) {
1337 *(command_line_i++) = *(shell_name++);
1338 }
1339
1340 *(command_line_i++) = ' ';
1341
1342 while(*full_exec_path) {
1343 *(command_line_i++) = *(full_exec_path++);
1344 }
1345
1346 if (*argv) {
1347 *(command_line_i++) = ' ';
1348 }
1349 }
1350
1351 argvi = argv;
1352 enclose_in_quotes_i = enclose_in_quotes;
1353
1354 while(*argvi) {
1355 char* p = *argvi;
1356 unsigned int backslash_count = 0;
1357
1358 if (*enclose_in_quotes_i) {
1359 *(command_line_i++) = '\"';
1360 }
1361
1362 while(*p) {
1363 if (*p == '\"') {
1364 if (cygwin_mode && have_sh) { /* HAVE_CYGWIN_SHELL */
1365 /* instead of a \", cygwin likes "" */
1366 *(command_line_i++) = '\"';
1367 } else {
1368
1369 /*
1370 * We have to insert a backslash for the "
1371 * and each \ that precedes the ".
1372 */
1373 backslash_count++;
1374
1375 while(backslash_count) {
1376 *(command_line_i++) = '\\';
1377 backslash_count--;
1378 };
1379 }
1380#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
1381 } else if (*p == '\\') {
1382 backslash_count++;
1383 } else {
1384 backslash_count = 0;
1385#endif
1386 }
1387
1388 /*
1389 * Copy the character.
1390 */
1391 *(command_line_i++) = *(p++);
1392 }
1393
1394 if (*enclose_in_quotes_i) {
1395#if !defined(HAVE_MKS_SHELL) && !defined(HAVE_CYGWIN_SHELL)
1396 /*
1397 * Add one \ for each \ that precedes the
1398 * closing ".
1399 */
1400 while(backslash_count--) {
1401 *(command_line_i++) = '\\';
1402 };
1403#endif
1404#ifdef KMK
1405 /*
1406 * ash it put off by echo "hello world" ending up as:
1407 * G:/.../kmk_ash.exe -c "echo ""hello world"""
1408 * It wants a space before the last '"'.
1409 * (The 'test_shell' goals in Makefile.kmk tests this problem.)
1410 */
1411 if (command_line_i[-1] == '\"' /* && cygwin_mode && have_sh*/ && !argvi[1]) {
1412 *(command_line_i++) = ' ';
1413 }
1414#endif
1415 *(command_line_i++) = '\"';
1416 }
1417
1418 /*
1419 * Append an intervening space.
1420 */
1421 if (*(++argvi)) {
1422 *(command_line_i++) = ' ';
1423 }
1424
1425 enclose_in_quotes_i++;
1426 }
1427
1428 /*
1429 * Append the terminating NULL.
1430 */
1431 *command_line_i = '\0';
1432
1433 if (enclose_in_quotes) free(enclose_in_quotes);
1434 return command_line;
1435}
1436
1437/*
1438 * Description: Given an argv and optional envp, launch the process
1439 * using the default stdin, stdout, and stderr handles.
1440 * Also, register process so that process_wait_for_any_private()
1441 * can be used via process_file_io(NULL) or
1442 * process_wait_for_any().
1443 *
1444 * Returns:
1445 *
1446 * Notes/Dependencies:
1447 */
1448HANDLE
1449process_easy(
1450 char **argv,
1451 char **envp)
1452{
1453#ifndef KMK
1454 HANDLE hIn;
1455 HANDLE hOut;
1456 HANDLE hErr;
1457#endif
1458 HANDLE hProcess;
1459
1460 if (proc_index >= MAXIMUM_WAIT_OBJECTS) {
1461 DB (DB_JOBS, ("process_easy: All process slots used up\n"));
1462 return INVALID_HANDLE_VALUE;
1463 }
1464#ifndef KMK
1465 if (DuplicateHandle(GetCurrentProcess(),
1466 GetStdHandle(STD_INPUT_HANDLE),
1467 GetCurrentProcess(),
1468 &hIn,
1469 0,
1470 TRUE,
1471 DUPLICATE_SAME_ACCESS) == FALSE) {
1472 fprintf(stderr,
1473 "process_easy: DuplicateHandle(In) failed (e=%ld)\n",
1474 GetLastError());
1475 return INVALID_HANDLE_VALUE;
1476 }
1477 if (DuplicateHandle(GetCurrentProcess(),
1478 GetStdHandle(STD_OUTPUT_HANDLE),
1479 GetCurrentProcess(),
1480 &hOut,
1481 0,
1482 TRUE,
1483 DUPLICATE_SAME_ACCESS) == FALSE) {
1484 fprintf(stderr,
1485 "process_easy: DuplicateHandle(Out) failed (e=%ld)\n",
1486 GetLastError());
1487 return INVALID_HANDLE_VALUE;
1488 }
1489 if (DuplicateHandle(GetCurrentProcess(),
1490 GetStdHandle(STD_ERROR_HANDLE),
1491 GetCurrentProcess(),
1492 &hErr,
1493 0,
1494 TRUE,
1495 DUPLICATE_SAME_ACCESS) == FALSE) {
1496 fprintf(stderr,
1497 "process_easy: DuplicateHandle(Err) failed (e=%ld)\n",
1498 GetLastError());
1499 return INVALID_HANDLE_VALUE;
1500 }
1501
1502 hProcess = process_init_fd(hIn, hOut, hErr);
1503#else
1504 hProcess = process_init_fd(0, 0, 0);
1505#endif /* !KMK */
1506
1507 if (process_begin(hProcess, argv, envp, argv[0], NULL)) {
1508 fake_exits_pending++;
1509 /* process_begin() failed: make a note of that. */
1510 if (!((sub_process*) hProcess)->last_err)
1511 ((sub_process*) hProcess)->last_err = -1;
1512#ifdef KMK
1513 if (!((sub_process*) hProcess)->exit_code)
1514#endif
1515 ((sub_process*) hProcess)->exit_code = process_last_err(hProcess);
1516
1517#ifndef KMK
1518 /* close up unused handles */
1519 CloseHandle(hIn);
1520 CloseHandle(hOut);
1521 CloseHandle(hErr);
1522#endif
1523 }
1524
1525 process_register(hProcess);
1526
1527 return hProcess;
1528}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette