bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / srclib / apr / threadproc / win32 / proc.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "win32/apr_arch_threadproc.h"
18 #include "win32/apr_arch_file_io.h"
19
20 #include "apr_thread_proc.h"
21 #include "apr_file_io.h"
22 #include "apr_general.h"
23 #include "apr_strings.h"
24 #include "apr_portable.h"
25 #include "apr_lib.h"
26 #include <stdlib.h>
27 #if APR_HAVE_SIGNAL_H
28 #include <signal.h>
29 #endif
30 #include <string.h>
31 #if APR_HAVE_PROCESS_H
32 #include <process.h>
33 #endif
34
35 /* We have very carefully excluded volumes of definitions from the
36  * Microsoft Platform SDK, which kill the build time performance.
37  * These the sole constants we borrow from WinBase.h and WinUser.h
38  */
39 #ifndef LOGON32_LOGON_NETWORK
40 #define LOGON32_LOGON_NETWORK 3
41 #endif
42
43 #ifdef _WIN32_WCE
44 #ifndef DETACHED_PROCESS
45 #define DETACHED_PROCESS 0
46 #endif
47 #ifndef CREATE_UNICODE_ENVIRONMENT
48 #define CREATE_UNICODE_ENVIRONMENT 0
49 #endif
50 #ifndef STARTF_USESHOWWINDOW
51 #define STARTF_USESHOWWINDOW 0
52 #endif
53 #ifndef SW_HIDE
54 #define SW_HIDE 0
55 #endif
56 #endif
57 /* 
58  * some of the ideas expressed herein are based off of Microsoft
59  * Knowledge Base article: Q190351
60  *
61  */
62
63 APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new,
64                                                   apr_pool_t *pool)
65 {
66     (*new) = (apr_procattr_t *)apr_pcalloc(pool, sizeof(apr_procattr_t));
67     (*new)->pool = pool;
68     (*new)->cmdtype = APR_PROGRAM;
69     return APR_SUCCESS;
70 }
71
72 APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr,
73                                               apr_int32_t in, 
74                                               apr_int32_t out,
75                                               apr_int32_t err)
76 {
77     apr_status_t stat = APR_SUCCESS;
78
79     if (in) {
80         /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while
81          * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so we
82          * must transpose the CHILD/PARENT blocking flags
83          * only for the stdin pipe.  stdout/stderr naturally
84          * map to the correct mode.
85          */
86         if (in == APR_CHILD_BLOCK)
87             in = APR_READ_BLOCK;
88         else if (in == APR_PARENT_BLOCK)
89             in = APR_WRITE_BLOCK;
90
91         stat = apr_create_nt_pipe(&attr->child_in, &attr->parent_in,
92                                   in, attr->pool);
93         if (stat == APR_SUCCESS)
94             stat = apr_file_inherit_unset(attr->parent_in);
95     }
96     if (out && stat == APR_SUCCESS) {
97         stat = apr_create_nt_pipe(&attr->parent_out, &attr->child_out,
98                                   out, attr->pool);
99         if (stat == APR_SUCCESS)
100             stat = apr_file_inherit_unset(attr->parent_out);
101     }
102     if (err && stat == APR_SUCCESS) {
103         stat = apr_create_nt_pipe(&attr->parent_err, &attr->child_err,
104                                   err, attr->pool);
105         if (stat == APR_SUCCESS)
106             stat = apr_file_inherit_unset(attr->parent_err);
107     }
108     return stat;
109 }
110
111 APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, 
112                                                   apr_file_t *child_in, 
113                                                   apr_file_t *parent_in)
114 {
115     apr_status_t rv = APR_SUCCESS;
116
117     if (child_in) {
118         if (attr->child_in == NULL)
119             rv = apr_file_dup(&attr->child_in, child_in, attr->pool);
120         else
121             rv = apr_file_dup2(attr->child_in, child_in, attr->pool);
122
123         if (rv == APR_SUCCESS)
124             rv = apr_file_inherit_set(attr->child_in);
125     }
126
127     if (parent_in && rv == APR_SUCCESS) {
128         if (attr->parent_in == NULL)
129             rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool);
130         else
131             rv = apr_file_dup2(attr->parent_in, parent_in, attr->pool);
132     }
133
134     return rv;
135 }
136
137 APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr,
138                                                    apr_file_t *child_out,
139                                                    apr_file_t *parent_out)
140 {
141     apr_status_t rv = APR_SUCCESS;
142
143     if (child_out) {
144         if (attr->child_out == NULL)
145             rv = apr_file_dup(&attr->child_out, child_out, attr->pool);
146         else
147             rv = apr_file_dup2(attr->child_out, child_out, attr->pool);
148
149         if (rv == APR_SUCCESS)
150             rv = apr_file_inherit_set(attr->child_out);
151     }
152
153     if (parent_out && rv == APR_SUCCESS) {
154         if (attr->parent_out == NULL)
155             rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool);
156         else
157             rv = apr_file_dup2(attr->parent_out, parent_out, attr->pool);
158     }
159
160     return rv;
161 }
162
163 APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr,
164                                                    apr_file_t *child_err,
165                                                    apr_file_t *parent_err)
166 {
167     apr_status_t rv = APR_SUCCESS;
168
169     if (child_err) {
170         if (attr->child_err == NULL)
171             rv = apr_file_dup(&attr->child_err, child_err, attr->pool);
172         else
173             rv = apr_file_dup2(attr->child_err, child_err, attr->pool);
174
175         if (rv == APR_SUCCESS)
176             rv = apr_file_inherit_set(attr->child_err);
177     }
178
179     if (parent_err && rv == APR_SUCCESS) {
180         if (attr->parent_err == NULL)
181             rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool);
182         else
183             rv = apr_file_dup2(attr->parent_err, parent_err, attr->pool);
184     }
185
186     return rv;
187 }
188
189 APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr,
190                                               const char *dir) 
191 {
192     /* curr dir must be in native format, there are all sorts of bugs in
193      * the NT library loading code that flunk the '/' parsing test.
194      */
195     return apr_filepath_merge(&attr->currdir, NULL, dir, 
196                               APR_FILEPATH_NATIVE, attr->pool);
197 }
198
199 APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr,
200                                                   apr_cmdtype_e cmd) 
201 {
202     attr->cmdtype = cmd;
203     return APR_SUCCESS;
204 }
205
206 APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr,
207                                                  apr_int32_t det) 
208 {
209     attr->detached = det;
210     return APR_SUCCESS;
211 }
212
213 static const char* has_space(const char *str)
214 {
215     const char *ch;
216     for (ch = str; *ch; ++ch) {
217         if (apr_isspace(*ch)) {
218             return ch;
219         }
220     }
221     return NULL;
222 }
223
224 static char *apr_caret_escape_args(apr_pool_t *p, const char *str)
225 {
226     char *cmd;
227     unsigned char *d;
228     const unsigned char *s;
229
230     cmd = apr_palloc(p, 2 * strlen(str) + 1);   /* Be safe */
231     d = (unsigned char *)cmd;
232     s = (const unsigned char *)str;
233     for (; *s; ++s) {
234
235         /* 
236          * Newlines to Win32/OS2 CreateProcess() are ill advised.
237          * Convert them to spaces since they are effectively white
238          * space to most applications
239          */
240         if (*s == '\r' || *s == '\n') {
241             *d++ = ' ';
242             continue;
243         }
244
245         if (IS_SHCHAR(*s)) {
246             *d++ = '^';
247         }
248         *d++ = *s;
249     }
250     *d = '\0';
251
252     return cmd;
253 }
254
255 APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr,
256                                                        apr_child_errfn_t *errfn)
257 {
258     attr->errfn = errfn;
259     return APR_SUCCESS;
260 }
261
262 APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr,
263                                                        apr_int32_t chk)
264 {
265     attr->errchk = chk;
266     return APR_SUCCESS;
267 }
268
269 APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr,
270                                                        apr_int32_t addrspace)
271 {
272     /* won't ever be used on this platform, so don't save the flag */
273     return APR_SUCCESS;
274 }
275
276 #if APR_HAS_UNICODE_FS && !defined(_WIN32_WCE)
277
278 /* Used only for the NT code path, a critical section is the fastest
279  * implementation available.
280  */
281 static CRITICAL_SECTION proc_lock;
282
283 static apr_status_t threadproc_global_cleanup(void *ignored)
284 {
285     DeleteCriticalSection(&proc_lock);
286     return APR_SUCCESS;
287 }
288
289 /* Called from apr_initialize, we need a critical section to handle
290  * the pipe inheritance on win32.  This will mutex any process create
291  * so as we change our inherited pipes, we prevent another process from
292  * also inheriting those alternate handles, and prevent the other process
293  * from failing to inherit our standard handles.
294  */
295 apr_status_t apr_threadproc_init(apr_pool_t *pool)
296 {
297     IF_WIN_OS_IS_UNICODE
298     {
299         InitializeCriticalSection(&proc_lock);
300         /* register the cleanup */
301         apr_pool_cleanup_register(pool, &proc_lock,
302                                   threadproc_global_cleanup,
303                                   apr_pool_cleanup_null);
304     }
305     return APR_SUCCESS;
306 }
307
308 #else /* !APR_HAS_UNICODE_FS || defined(_WIN32_WCE) */
309
310 apr_status_t apr_threadproc_init(apr_pool_t *pool)
311 {
312     return APR_SUCCESS;
313 }
314
315 #endif
316
317 APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new,
318                                           const char *progname,
319                                           const char * const *args,
320                                           const char * const *env,
321                                           apr_procattr_t *attr,
322                                           apr_pool_t *pool)
323 {
324     apr_status_t rv;
325     apr_size_t i;
326     const char *argv0;
327     char *cmdline;
328     char *pEnvBlock;
329     PROCESS_INFORMATION pi;
330     DWORD dwCreationFlags = 0;
331
332     new->in = attr->parent_in;
333     new->out = attr->parent_out;
334     new->err = attr->parent_err;
335
336     if (attr->detached) {
337         /* If we are creating ourselves detached, Then we should hide the
338          * window we are starting in.  And we had better redfine our
339          * handles for STDIN, STDOUT, and STDERR. Do not set the
340          * detached attribute for Win9x. We have found that Win9x does
341          * not manage the stdio handles properly when running old 16
342          * bit executables if the detached attribute is set.
343          */
344         if (apr_os_level >= APR_WIN_NT) {
345             /* 
346              * XXX DETACHED_PROCESS won't on Win9x at all; on NT/W2K 
347              * 16 bit executables fail (MS KB: Q150956)
348              */
349             dwCreationFlags |= DETACHED_PROCESS;
350         }
351     }
352
353     /* progname must be unquoted, in native format, as there are all sorts 
354      * of bugs in the NT library loader code that fault when parsing '/'.
355      * XXX progname must be NULL if this is a 16 bit app running in WOW
356      */
357     if (progname[0] == '\"') {
358         progname = apr_pstrndup(pool, progname + 1, strlen(progname) - 2);
359     }
360
361     if (attr->cmdtype == APR_PROGRAM || attr->cmdtype == APR_PROGRAM_ENV) {
362         char *fullpath = NULL;
363         if ((rv = apr_filepath_merge(&fullpath, attr->currdir, progname, 
364                                      APR_FILEPATH_NATIVE, pool)) != APR_SUCCESS) {
365             if (attr->errfn) {
366                 attr->errfn(pool, rv, 
367                             apr_pstrcat(pool, "filepath_merge failed.", 
368                                         " currdir: ", attr->currdir, 
369                                         " progname: ", progname,NULL));
370             }
371             return rv;
372         }
373         progname = fullpath;
374     } 
375     else {
376         /* Do not fail if the path isn't parseable for APR_PROGRAM_PATH
377          * or APR_SHELLCMD.  We only invoke apr_filepath_merge (with no
378          * left hand side expression) in order to correct the path slash
379          * delimiters.  But the filename doesn't need to be in the CWD,
380          * nor does it need to be a filename at all (it could be a
381          * built-in shell command.)
382          */
383         char *fullpath = NULL;
384         if ((rv = apr_filepath_merge(&fullpath, "", progname, 
385                                      APR_FILEPATH_NATIVE, pool)) == APR_SUCCESS) {
386             progname = fullpath;
387         }        
388     }
389
390     if (has_space(progname)) {
391         argv0 = apr_pstrcat(pool, "\"", progname, "\"", NULL);
392     }
393     else {
394         argv0 = progname;
395     }
396
397     /* Handle the args, seperate from argv0 */
398     cmdline = "";
399     for (i = 1; args && args[i]; ++i) {
400         if (has_space(args[i]) || !args[i][0]) {
401             cmdline = apr_pstrcat(pool, cmdline, " \"", args[i], "\"", NULL);
402         }
403         else {
404             cmdline = apr_pstrcat(pool, cmdline, " ", args[i], NULL);
405         }
406     }
407
408 #ifndef _WIN32_WCE
409     if (attr->cmdtype == APR_SHELLCMD || attr->cmdtype == APR_SHELLCMD_ENV) {
410         char *shellcmd = getenv("COMSPEC");
411         if (!shellcmd) {
412             if (attr->errfn) {
413                 attr->errfn(pool, APR_EINVAL, "COMSPEC envar is not set");
414             }
415             return APR_EINVAL;
416         }
417         if (shellcmd[0] == '"') {
418             progname = apr_pstrndup(pool, shellcmd + 1, strlen(shellcmd) - 2);
419         }
420         else {
421             progname = shellcmd;
422             if (has_space(shellcmd)) {
423                 shellcmd = apr_pstrcat(pool, "\"", shellcmd, "\"", NULL);
424             }
425         }
426         /* Command.com does not support a quoted command, while cmd.exe demands one.
427          */
428         i = strlen(progname);
429         if (i >= 11 && strcasecmp(progname + i - 11, "command.com") == 0) {
430             cmdline = apr_pstrcat(pool, shellcmd, " /C ", argv0, cmdline, NULL);
431         }
432         else {
433             cmdline = apr_pstrcat(pool, shellcmd, " /C \"", argv0, cmdline, "\"", NULL);
434         }
435     } 
436     else 
437 #endif
438     {
439 #if defined(_WIN32_WCE)
440         {
441 #else
442         /* Win32 is _different_ than unix.  While unix will find the given
443          * program since it's already chdir'ed, Win32 cannot since the parent
444          * attempts to open the program with it's own path.
445          * ###: This solution isn't much better - it may defeat path searching
446          * when the path search was desired.  Open to further discussion.
447          */
448         i = strlen(progname);
449         if (i >= 4 && (strcasecmp(progname + i - 4, ".bat") == 0
450                     || strcasecmp(progname + i - 4, ".cmd") == 0))
451         {
452             char *shellcmd = getenv("COMSPEC");
453             if (!shellcmd) {
454                 if (attr->errfn) {
455                     attr->errfn(pool, APR_EINVAL, "COMSPEC envar is not set");
456                 }
457                 return APR_EINVAL;
458             }
459             if (shellcmd[0] == '"') {
460                 progname = apr_pstrndup(pool, shellcmd + 1, strlen(shellcmd) - 2);
461             }
462             else {
463                 progname = shellcmd;
464                 if (has_space(shellcmd)) {
465                     shellcmd = apr_pstrcat(pool, "\"", shellcmd, "\"", NULL);
466                 }
467             }
468             i = strlen(progname);
469             if (i >= 11 && strcasecmp(progname + i - 11, "command.com") == 0) {
470                 /* XXX: Still insecure - need doubled-quotes on each individual
471                  * arg of cmdline.  Suspect we need to postpone cmdline parsing
472                  * until this moment in all four code paths, with some flags
473                  * to toggle 'which flavor' is needed.
474                  */
475                 cmdline = apr_pstrcat(pool, shellcmd, " /C ", argv0, cmdline, NULL);
476             }
477             else {
478                 /* We must protect the cmdline args from any interpolation - this
479                  * is not a shellcmd, and the source of argv[] is untrusted.
480                  * Notice we escape ALL the cmdline args, including the quotes
481                  * around the individual args themselves.  No sense in allowing
482                  * the shift-state to be toggled, and the application will 
483                  * not see the caret escapes.
484                  */
485                 cmdline = apr_caret_escape_args(pool, cmdline);
486                 /*
487                  * Our app name must always be quoted so the quotes surrounding
488                  * the entire /c "command args" are unambigious.
489                  */
490                 if (*argv0 != '"') {
491                     cmdline = apr_pstrcat(pool, shellcmd, " /C \"\"", argv0, "\"", cmdline, "\"", NULL);
492                 }
493                 else {
494                     cmdline = apr_pstrcat(pool, shellcmd, " /C \"", argv0, cmdline, "\"", NULL);
495                 }
496             }
497         }
498         else {
499 #endif
500             /* A simple command we are directly invoking.  Do not pass
501              * the first arg to CreateProc() for APR_PROGRAM_PATH
502              * invocation, since it would need to be a literal and
503              * complete file path.  That is; "c:\bin\aprtest.exe"
504              * would succeed, but "c:\bin\aprtest" or "aprtest.exe"
505              * can fail.
506              */
507             cmdline = apr_pstrcat(pool, argv0, cmdline, NULL);
508
509             if (attr->cmdtype == APR_PROGRAM_PATH) {
510                 progname = NULL;
511             }
512         }
513     }
514
515     if (!env || attr->cmdtype == APR_PROGRAM_ENV ||
516         attr->cmdtype == APR_SHELLCMD_ENV) {
517         pEnvBlock = NULL;
518     }
519     else {
520         apr_size_t iEnvBlockLen;
521         /*
522          * Win32's CreateProcess call requires that the environment
523          * be passed in an environment block, a null terminated block of
524          * null terminated strings.
525          */  
526         i = 0;
527         iEnvBlockLen = 1;
528         while (env[i]) {
529             iEnvBlockLen += strlen(env[i]) + 1;
530             i++;
531         }
532         if (!i) 
533             ++iEnvBlockLen;
534
535 #if APR_HAS_UNICODE_FS
536         IF_WIN_OS_IS_UNICODE
537         {
538             apr_wchar_t *pNext;
539             pEnvBlock = (char *)apr_palloc(pool, iEnvBlockLen * 2);
540             dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
541
542             i = 0;
543             pNext = (apr_wchar_t*)pEnvBlock;
544             while (env[i]) {
545                 apr_size_t in = strlen(env[i]) + 1;
546                 if ((rv = apr_conv_utf8_to_ucs2(env[i], &in, 
547                                                 pNext, &iEnvBlockLen)) 
548                         != APR_SUCCESS) {
549                     if (attr->errfn) {
550                         attr->errfn(pool, rv, 
551                                     apr_pstrcat(pool, 
552                                                 "utf8 to ucs2 conversion failed" 
553                                                 " on this string: ", env[i], NULL));
554                     }
555                     return rv;
556                 }
557                 pNext = wcschr(pNext, L'\0') + 1;
558                 i++;
559             }
560             if (!i)
561                 *(pNext++) = L'\0';
562             *pNext = L'\0';
563         }
564 #endif /* APR_HAS_UNICODE_FS */
565 #if APR_HAS_ANSI_FS
566         ELSE_WIN_OS_IS_ANSI
567         {
568             char *pNext;
569             pEnvBlock = (char *)apr_palloc(pool, iEnvBlockLen);
570     
571             i = 0;
572             pNext = pEnvBlock;
573             while (env[i]) {
574                 strcpy(pNext, env[i]);
575                 pNext = strchr(pNext, '\0') + 1;
576                 i++;
577             }
578             if (!i)
579                 *(pNext++) = '\0';
580             *pNext = '\0';
581         }
582 #endif /* APR_HAS_ANSI_FS */
583     } 
584
585     new->invoked = cmdline;
586
587 #if APR_HAS_UNICODE_FS
588     IF_WIN_OS_IS_UNICODE
589     {
590         STARTUPINFOW si;
591         DWORD stdin_reset = 0;
592         DWORD stdout_reset = 0;
593         DWORD stderr_reset = 0;
594         apr_wchar_t *wprg = NULL;
595         apr_wchar_t *wcmd = NULL;
596         apr_wchar_t *wcwd = NULL;
597
598         if (progname) {
599             apr_size_t nprg = strlen(progname) + 1;
600             apr_size_t nwprg = nprg + 6;
601             wprg = apr_palloc(pool, nwprg * sizeof(wprg[0]));
602             if ((rv = apr_conv_utf8_to_ucs2(progname, &nprg, wprg, &nwprg))
603                    != APR_SUCCESS) {
604                 if (attr->errfn) {
605                     attr->errfn(pool, rv, 
606                                 apr_pstrcat(pool, 
607                                             "utf8 to ucs2 conversion failed" 
608                                             " on progname: ", progname, NULL));
609                 }
610                 return rv;
611             }
612         }
613
614         if (cmdline) {
615             apr_size_t ncmd = strlen(cmdline) + 1;
616             apr_size_t nwcmd = ncmd;
617             wcmd = apr_palloc(pool, nwcmd * sizeof(wcmd[0]));
618             if ((rv = apr_conv_utf8_to_ucs2(cmdline, &ncmd, wcmd, &nwcmd))
619                     != APR_SUCCESS) {
620                 if (attr->errfn) {
621                     attr->errfn(pool, rv, 
622                                 apr_pstrcat(pool, 
623                                             "utf8 to ucs2 conversion failed" 
624                                             " on cmdline: ", cmdline, NULL));
625                 }
626                 return rv;
627             }
628         }
629
630         if (attr->currdir)
631         {
632             apr_size_t ncwd = strlen(attr->currdir) + 1;
633             apr_size_t nwcwd = ncwd;
634             wcwd = apr_palloc(pool, ncwd * sizeof(wcwd[0]));
635             if ((rv = apr_conv_utf8_to_ucs2(attr->currdir, &ncwd, 
636                                             wcwd, &nwcwd))
637                     != APR_SUCCESS) {
638                 if (attr->errfn) {
639                     attr->errfn(pool, rv, 
640                                 apr_pstrcat(pool, 
641                                             "utf8 to ucs2 conversion failed" 
642                                             " on currdir: ", attr->currdir, NULL));
643                 }
644                 return rv;
645             }
646         }
647
648         memset(&si, 0, sizeof(si));
649         si.cb = sizeof(si);
650
651         if (attr->detached) {
652             si.dwFlags |= STARTF_USESHOWWINDOW;
653             si.wShowWindow = SW_HIDE;
654         }
655
656 #ifndef _WIN32_WCE
657         /* LOCK CRITICAL SECTION 
658          * before we begin to manipulate the inherited handles
659          */
660         EnterCriticalSection(&proc_lock);
661
662         if ((attr->child_in && attr->child_in->filehand)
663             || (attr->child_out && attr->child_out->filehand)
664             || (attr->child_err && attr->child_err->filehand))
665         {
666             si.dwFlags |= STARTF_USESTDHANDLES;
667
668             si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
669             if (GetHandleInformation(si.hStdInput, &stdin_reset)
670                     && (stdin_reset &= HANDLE_FLAG_INHERIT))
671                 SetHandleInformation(si.hStdInput,
672                                      HANDLE_FLAG_INHERIT, 0);
673
674             if (attr->child_in && attr->child_in->filehand)
675             {
676                 si.hStdInput = attr->child_in->filehand;
677                 SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT,
678                                                    HANDLE_FLAG_INHERIT);
679             }
680             else
681                 si.hStdInput = INVALID_HANDLE_VALUE;
682             
683             si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
684             if (GetHandleInformation(si.hStdOutput, &stdout_reset)
685                     && (stdout_reset &= HANDLE_FLAG_INHERIT))
686                 SetHandleInformation(si.hStdOutput,
687                                      HANDLE_FLAG_INHERIT, 0);
688
689             if (attr->child_out && attr->child_out->filehand)
690             {
691                 si.hStdOutput = attr->child_out->filehand;
692                 SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT,
693                                                     HANDLE_FLAG_INHERIT);
694             }
695             else
696                 si.hStdOutput = INVALID_HANDLE_VALUE;
697
698             si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
699             if (GetHandleInformation(si.hStdError, &stderr_reset)
700                     && (stderr_reset &= HANDLE_FLAG_INHERIT))
701                 SetHandleInformation(si.hStdError,
702                                      HANDLE_FLAG_INHERIT, 0);
703
704             if (attr->child_err && attr->child_err->filehand)
705             {
706                 si.hStdError = attr->child_err->filehand;
707                 SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT,
708                                                    HANDLE_FLAG_INHERIT);
709             }
710             else
711                 si.hStdError = INVALID_HANDLE_VALUE;
712         }
713         rv = CreateProcessW(wprg, wcmd,        /* Executable & Command line */
714                             NULL, NULL,        /* Proc & thread security attributes */
715                             TRUE,              /* Inherit handles */
716                             dwCreationFlags,   /* Creation flags */
717                             pEnvBlock,         /* Environment block */
718                             wcwd,              /* Current directory name */
719                             &si, &pi);
720
721         if ((attr->child_in && attr->child_in->filehand)
722             || (attr->child_out && attr->child_out->filehand)
723             || (attr->child_err && attr->child_err->filehand))
724         {
725             if (stdin_reset)
726                 SetHandleInformation(GetStdHandle(STD_INPUT_HANDLE),
727                                      stdin_reset, stdin_reset);
728
729             if (stdout_reset)
730                 SetHandleInformation(GetStdHandle(STD_OUTPUT_HANDLE),
731                                      stdout_reset, stdout_reset);
732
733             if (stderr_reset)
734                 SetHandleInformation(GetStdHandle(STD_ERROR_HANDLE),
735                                      stderr_reset, stderr_reset);
736         }
737         /* RELEASE CRITICAL SECTION 
738          * The state of the inherited handles has been restored.
739          */
740         LeaveCriticalSection(&proc_lock);
741
742 #else /* defined(_WIN32_WCE) */
743         rv = CreateProcessW(wprg, wcmd,        /* Executable & Command line */
744                             NULL, NULL,        /* Proc & thread security attributes */
745                             FALSE,             /* must be 0 */
746                             dwCreationFlags,   /* Creation flags */
747                             NULL,              /* Environment block must be NULL */
748                             NULL,              /* Current directory name must be NULL*/
749                             NULL,              /* STARTUPINFO not supported */
750                             &pi);
751 #endif
752     }
753 #endif /* APR_HAS_UNICODE_FS */
754 #if APR_HAS_ANSI_FS
755     ELSE_WIN_OS_IS_ANSI
756     {
757         STARTUPINFOA si;
758         memset(&si, 0, sizeof(si));
759         si.cb = sizeof(si);
760
761         if (attr->detached) {
762             si.dwFlags |= STARTF_USESHOWWINDOW;
763             si.wShowWindow = SW_HIDE;
764         }
765
766         if ((attr->child_in && attr->child_in->filehand)
767             || (attr->child_out && attr->child_out->filehand)
768             || (attr->child_err && attr->child_err->filehand))
769         {
770             si.dwFlags |= STARTF_USESTDHANDLES;
771
772             si.hStdInput = (attr->child_in) 
773                               ? attr->child_in->filehand
774                               : GetStdHandle(STD_INPUT_HANDLE);
775
776             si.hStdOutput = (attr->child_out)
777                               ? attr->child_out->filehand
778                               : GetStdHandle(STD_OUTPUT_HANDLE);
779
780             si.hStdError = (attr->child_err)
781                               ? attr->child_err->filehand
782                               : GetStdHandle(STD_ERROR_HANDLE);
783         }
784
785         rv = CreateProcessA(progname, cmdline, /* Command line */
786                             NULL, NULL,        /* Proc & thread security attributes */
787                             TRUE,              /* Inherit handles */
788                             dwCreationFlags,   /* Creation flags */
789                             pEnvBlock,         /* Environment block */
790                             attr->currdir,     /* Current directory name */
791                             &si, &pi);
792     }
793 #endif /* APR_HAS_ANSI_FS */
794
795     /* Check CreateProcess result 
796      */
797     if (!rv)
798         return apr_get_os_error();
799
800     /* XXX Orphaned handle warning - no fix due to broken apr_proc_t api.
801      */
802     new->hproc = pi.hProcess;
803     new->pid = pi.dwProcessId;
804
805     if (attr->child_in) {
806         apr_file_close(attr->child_in);
807     }
808     if (attr->child_out) {
809         apr_file_close(attr->child_out);
810     }
811     if (attr->child_err) {
812         apr_file_close(attr->child_err);
813     }
814     CloseHandle(pi.hThread);
815
816     return APR_SUCCESS;
817 }
818
819 APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc,
820                                                   int *exitcode,
821                                                   apr_exit_why_e *exitwhy,
822                                                   apr_wait_how_e waithow,
823                                                   apr_pool_t *p)
824 {
825     /* Unix does apr_proc_wait(proc(-1), exitcode, exitwhy, waithow)
826      * but Win32's apr_proc_wait won't work that way.  We can either
827      * register all APR created processes in some sort of AsyncWait
828      * thread, or simply walk from the global process pool for all 
829      * apr_pool_note_subprocess()es registered with APR.
830      */
831     return APR_ENOTIMPL;
832 }
833
834 static apr_exit_why_e why_from_exit_code(DWORD exit) {
835     /* See WinNT.h STATUS_ACCESS_VIOLATION and family for how
836      * this class of failures was determined
837      */
838     if (((exit & 0xC0000000) == 0xC0000000) 
839                     && !(exit & 0x3FFF0000))
840         return APR_PROC_SIGNAL;
841     else
842         return APR_PROC_EXIT;
843
844     /* ### No way to tell if Dr Watson grabbed a core, AFAICT. */
845 }
846
847 APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc,
848                                         int *exitcode, apr_exit_why_e *exitwhy,
849                                         apr_wait_how_e waithow)
850 {
851     DWORD stat;
852     DWORD time;
853
854     if (waithow == APR_WAIT)
855         time = INFINITE;
856     else
857         time = 0;
858
859     if ((stat = WaitForSingleObject(proc->hproc, time)) == WAIT_OBJECT_0) {
860         if (GetExitCodeProcess(proc->hproc, &stat)) {
861             if (exitcode)
862                 *exitcode = stat;
863             if (exitwhy)
864                 *exitwhy = why_from_exit_code(stat);
865             CloseHandle(proc->hproc);
866             proc->hproc = NULL;
867             return APR_CHILD_DONE;
868         }
869     }
870     else if (stat == WAIT_TIMEOUT) {
871         return APR_CHILD_NOTDONE;
872     }
873     return apr_get_os_error();
874 }