xref: /aosp_15_r20/external/cronet/third_party/apache-portable-runtime/src/threadproc/win32/proc.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 "apr_arch_threadproc.h"
18 #include "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 /* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE
36  * requested for a specific child handle;
37  */
38 static apr_file_t no_file = { NULL, INVALID_HANDLE_VALUE, };
39 
40 /* We have very carefully excluded volumes of definitions from the
41  * Microsoft Platform SDK, which kill the build time performance.
42  * These the sole constants we borrow from WinBase.h and WinUser.h
43  */
44 #ifndef LOGON32_LOGON_NETWORK
45 #define LOGON32_LOGON_NETWORK 3
46 #endif
47 
48 #ifdef _WIN32_WCE
49 #ifndef DETACHED_PROCESS
50 #define DETACHED_PROCESS 0
51 #endif
52 #ifndef CREATE_UNICODE_ENVIRONMENT
53 #define CREATE_UNICODE_ENVIRONMENT 0
54 #endif
55 #ifndef STARTF_USESHOWWINDOW
56 #define STARTF_USESHOWWINDOW 0
57 #endif
58 #ifndef SW_HIDE
59 #define SW_HIDE 0
60 #endif
61 #endif
62 
63 /*
64  * some of the ideas expressed herein are based off of Microsoft
65  * Knowledge Base article: Q190351
66  *
67  */
apr_procattr_create(apr_procattr_t ** new,apr_pool_t * pool)68 APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new,
69                                                   apr_pool_t *pool)
70 {
71     (*new) = (apr_procattr_t *)apr_pcalloc(pool, sizeof(apr_procattr_t));
72     (*new)->pool = pool;
73     (*new)->cmdtype = APR_PROGRAM;
74     return APR_SUCCESS;
75 }
76 
apr_procattr_io_set(apr_procattr_t * attr,apr_int32_t in,apr_int32_t out,apr_int32_t err)77 APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr,
78                                               apr_int32_t in,
79                                               apr_int32_t out,
80                                               apr_int32_t err)
81 {
82     apr_status_t stat = APR_SUCCESS;
83 
84     if (in) {
85         /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while
86          * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose
87          * the CHILD/PARENT blocking flags for the stdin pipe.
88          * stdout/stderr map to the correct mode by default.
89          */
90         if (in == APR_CHILD_BLOCK)
91             in = APR_READ_BLOCK;
92         else if (in == APR_PARENT_BLOCK)
93             in = APR_WRITE_BLOCK;
94 
95         if (in == APR_NO_FILE)
96             attr->child_in = &no_file;
97         else {
98             stat = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in,
99                                            in, attr->pool);
100         }
101         if (stat == APR_SUCCESS)
102             stat = apr_file_inherit_unset(attr->parent_in);
103     }
104     if (out && stat == APR_SUCCESS) {
105         if (out == APR_NO_FILE)
106             attr->child_out = &no_file;
107         else {
108             stat = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out,
109                                            out, attr->pool);
110         }
111         if (stat == APR_SUCCESS)
112             stat = apr_file_inherit_unset(attr->parent_out);
113     }
114     if (err && stat == APR_SUCCESS) {
115         if (err == APR_NO_FILE)
116             attr->child_err = &no_file;
117         else {
118             stat = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err,
119                                            err, attr->pool);
120         }
121         if (stat == APR_SUCCESS)
122             stat = apr_file_inherit_unset(attr->parent_err);
123     }
124     return stat;
125 }
126 
apr_procattr_child_in_set(apr_procattr_t * attr,apr_file_t * child_in,apr_file_t * parent_in)127 APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr,
128                                                   apr_file_t *child_in,
129                                                   apr_file_t *parent_in)
130 {
131     apr_status_t rv = APR_SUCCESS;
132 
133     if (child_in) {
134         if ((attr->child_in == NULL) || (attr->child_in == &no_file))
135             rv = apr_file_dup(&attr->child_in, child_in, attr->pool);
136         else
137             rv = apr_file_dup2(attr->child_in, child_in, attr->pool);
138 
139         if (rv == APR_SUCCESS)
140             rv = apr_file_inherit_set(attr->child_in);
141     }
142 
143     if (parent_in && rv == APR_SUCCESS) {
144         if (attr->parent_in == NULL)
145             rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool);
146         else
147             rv = apr_file_dup2(attr->parent_in, parent_in, attr->pool);
148     }
149 
150     return rv;
151 }
152 
apr_procattr_child_out_set(apr_procattr_t * attr,apr_file_t * child_out,apr_file_t * parent_out)153 APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr,
154                                                    apr_file_t *child_out,
155                                                    apr_file_t *parent_out)
156 {
157     apr_status_t rv = APR_SUCCESS;
158 
159     if (child_out) {
160         if ((attr->child_out == NULL) || (attr->child_out == &no_file))
161             rv = apr_file_dup(&attr->child_out, child_out, attr->pool);
162         else
163             rv = apr_file_dup2(attr->child_out, child_out, attr->pool);
164 
165         if (rv == APR_SUCCESS)
166             rv = apr_file_inherit_set(attr->child_out);
167     }
168 
169     if (parent_out && rv == APR_SUCCESS) {
170         if (attr->parent_out == NULL)
171             rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool);
172         else
173             rv = apr_file_dup2(attr->parent_out, parent_out, attr->pool);
174     }
175 
176     return rv;
177 }
178 
apr_procattr_child_err_set(apr_procattr_t * attr,apr_file_t * child_err,apr_file_t * parent_err)179 APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr,
180                                                    apr_file_t *child_err,
181                                                    apr_file_t *parent_err)
182 {
183     apr_status_t rv = APR_SUCCESS;
184 
185     if (child_err) {
186         if ((attr->child_err == NULL) || (attr->child_err == &no_file))
187             rv = apr_file_dup(&attr->child_err, child_err, attr->pool);
188         else
189             rv = apr_file_dup2(attr->child_err, child_err, attr->pool);
190 
191         if (rv == APR_SUCCESS)
192             rv = apr_file_inherit_set(attr->child_err);
193     }
194 
195     if (parent_err && rv == APR_SUCCESS) {
196         if (attr->parent_err == NULL)
197             rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool);
198         else
199             rv = apr_file_dup2(attr->parent_err, parent_err, attr->pool);
200     }
201 
202     return rv;
203 }
204 
apr_procattr_dir_set(apr_procattr_t * attr,const char * dir)205 APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr,
206                                               const char *dir)
207 {
208     /* curr dir must be in native format, there are all sorts of bugs in
209      * the NT library loading code that flunk the '/' parsing test.
210      */
211     return apr_filepath_merge(&attr->currdir, NULL, dir,
212                               APR_FILEPATH_NATIVE, attr->pool);
213 }
214 
apr_procattr_cmdtype_set(apr_procattr_t * attr,apr_cmdtype_e cmd)215 APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr,
216                                                   apr_cmdtype_e cmd)
217 {
218     attr->cmdtype = cmd;
219     return APR_SUCCESS;
220 }
221 
apr_procattr_detach_set(apr_procattr_t * attr,apr_int32_t det)222 APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr,
223                                                  apr_int32_t det)
224 {
225     attr->detached = det;
226     return APR_SUCCESS;
227 }
228 
229 #ifndef _WIN32_WCE
attr_cleanup(void * theattr)230 static apr_status_t attr_cleanup(void *theattr)
231 {
232     apr_procattr_t *attr = (apr_procattr_t *)theattr;
233     if (attr->user_token)
234         CloseHandle(attr->user_token);
235     attr->user_token = NULL;
236     return APR_SUCCESS;
237 }
238 #endif
239 
apr_procattr_user_set(apr_procattr_t * attr,const char * username,const char * password)240 APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr,
241                                                 const char *username,
242                                                 const char *password)
243 {
244 #ifdef _WIN32_WCE
245     return APR_ENOTIMPL;
246 #else
247     HANDLE user;
248     apr_wchar_t *wusername = NULL;
249     apr_wchar_t *wpassword = NULL;
250     apr_status_t rv;
251     apr_size_t len, wlen;
252 
253     if (apr_os_level >= APR_WIN_NT_4)
254     {
255         if (attr->user_token) {
256             /* Cannot set that twice */
257             if (attr->errfn) {
258                 attr->errfn(attr->pool, 0,
259                             apr_pstrcat(attr->pool,
260                                         "function called twice"
261                                          " on username: ", username, NULL));
262             }
263             return APR_EINVAL;
264         }
265         len = strlen(username) + 1;
266         wlen = len;
267         wusername = apr_palloc(attr->pool, wlen * sizeof(apr_wchar_t));
268         if ((rv = apr_conv_utf8_to_ucs2(username, &len, wusername, &wlen))
269                    != APR_SUCCESS) {
270             if (attr->errfn) {
271                 attr->errfn(attr->pool, rv,
272                             apr_pstrcat(attr->pool,
273                                         "utf8 to ucs2 conversion failed"
274                                          " on username: ", username, NULL));
275             }
276             return rv;
277         }
278         if (password) {
279             len = strlen(password) + 1;
280             wlen = len;
281             wpassword = apr_palloc(attr->pool, wlen * sizeof(apr_wchar_t));
282             if ((rv = apr_conv_utf8_to_ucs2(password, &len, wpassword, &wlen))
283                        != APR_SUCCESS) {
284                 if (attr->errfn) {
285                     attr->errfn(attr->pool, rv,
286                                 apr_pstrcat(attr->pool,
287                                         "utf8 to ucs2 conversion failed"
288                                          " on password: ", password, NULL));
289                 }
290                 return rv;
291             }
292         }
293         if (!LogonUserW(wusername,
294                         NULL,
295                         wpassword ? wpassword : L"",
296                         LOGON32_LOGON_NETWORK,
297                         LOGON32_PROVIDER_DEFAULT,
298                         &user)) {
299             /* Logon Failed */
300             return apr_get_os_error();
301         }
302         if (wpassword)
303             memset(wpassword, 0, wlen * sizeof(apr_wchar_t));
304         /* Get the primary token for user */
305         if (!DuplicateTokenEx(user,
306                               TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY,
307                               NULL,
308                               SecurityImpersonation,
309                               TokenPrimary,
310                               &(attr->user_token))) {
311             /* Failed to duplicate the user token */
312             rv = apr_get_os_error();
313             CloseHandle(user);
314             return rv;
315         }
316         CloseHandle(user);
317 
318         attr->sd = apr_pcalloc(attr->pool, SECURITY_DESCRIPTOR_MIN_LENGTH);
319         InitializeSecurityDescriptor(attr->sd, SECURITY_DESCRIPTOR_REVISION);
320         SetSecurityDescriptorDacl(attr->sd, -1, 0, 0);
321         attr->sa = apr_palloc(attr->pool, sizeof(SECURITY_ATTRIBUTES));
322         attr->sa->nLength = sizeof (SECURITY_ATTRIBUTES);
323         attr->sa->lpSecurityDescriptor = attr->sd;
324         attr->sa->bInheritHandle = FALSE;
325 
326         /* register the cleanup */
327         apr_pool_cleanup_register(attr->pool, (void *)attr,
328                                   attr_cleanup,
329                                   apr_pool_cleanup_null);
330         return APR_SUCCESS;
331     }
332     else
333         return APR_ENOTIMPL;
334 #endif
335 }
336 
apr_procattr_group_set(apr_procattr_t * attr,const char * groupname)337 APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr,
338                                                  const char *groupname)
339 {
340     /* Always return SUCCESS cause groups are irrelevant */
341     return APR_SUCCESS;
342 }
343 
has_space(const char * str)344 static const char* has_space(const char *str)
345 {
346     const char *ch;
347     for (ch = str; *ch; ++ch) {
348         if (apr_isspace(*ch)) {
349             return ch;
350         }
351     }
352     return NULL;
353 }
354 
apr_caret_escape_args(apr_pool_t * p,const char * str)355 static char *apr_caret_escape_args(apr_pool_t *p, const char *str)
356 {
357     char *cmd;
358     unsigned char *d;
359     const unsigned char *s;
360 
361     cmd = apr_palloc(p, 2 * strlen(str) + 1);	/* Be safe */
362     d = (unsigned char *)cmd;
363     s = (const unsigned char *)str;
364     for (; *s; ++s) {
365 
366         /*
367          * Newlines to Win32/OS2 CreateProcess() are ill advised.
368          * Convert them to spaces since they are effectively white
369          * space to most applications
370          */
371 	if (*s == '\r' || *s == '\n') {
372 	    *d++ = ' ';
373             continue;
374 	}
375 
376 	if (IS_SHCHAR(*s)) {
377 	    *d++ = '^';
378 	}
379 	*d++ = *s;
380     }
381     *d = '\0';
382 
383     return cmd;
384 }
385 
apr_procattr_child_errfn_set(apr_procattr_t * attr,apr_child_errfn_t * errfn)386 APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr,
387                                                        apr_child_errfn_t *errfn)
388 {
389     attr->errfn = errfn;
390     return APR_SUCCESS;
391 }
392 
apr_procattr_error_check_set(apr_procattr_t * attr,apr_int32_t chk)393 APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr,
394                                                        apr_int32_t chk)
395 {
396     attr->errchk = chk;
397     return APR_SUCCESS;
398 }
399 
apr_procattr_addrspace_set(apr_procattr_t * attr,apr_int32_t addrspace)400 APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr,
401                                                        apr_int32_t addrspace)
402 {
403     /* won't ever be used on this platform, so don't save the flag */
404     return APR_SUCCESS;
405 }
406 
407 #if APR_HAS_UNICODE_FS && !defined(_WIN32_WCE)
408 
409 /* Used only for the NT code path, a critical section is the fastest
410  * implementation available.
411  */
412 static CRITICAL_SECTION proc_lock;
413 
threadproc_global_cleanup(void * ignored)414 static apr_status_t threadproc_global_cleanup(void *ignored)
415 {
416     DeleteCriticalSection(&proc_lock);
417     return APR_SUCCESS;
418 }
419 
420 /* Called from apr_initialize, we need a critical section to handle
421  * the pipe inheritance on win32.  This will mutex any process create
422  * so as we change our inherited pipes, we prevent another process from
423  * also inheriting those alternate handles, and prevent the other process
424  * from failing to inherit our standard handles.
425  */
apr_threadproc_init(apr_pool_t * pool)426 apr_status_t apr_threadproc_init(apr_pool_t *pool)
427 {
428     IF_WIN_OS_IS_UNICODE
429     {
430         InitializeCriticalSection(&proc_lock);
431         /* register the cleanup */
432         apr_pool_cleanup_register(pool, &proc_lock,
433                                   threadproc_global_cleanup,
434                                   apr_pool_cleanup_null);
435     }
436     return APR_SUCCESS;
437 }
438 
439 #else /* !APR_HAS_UNICODE_FS || defined(_WIN32_WCE) */
440 
apr_threadproc_init(apr_pool_t * pool)441 apr_status_t apr_threadproc_init(apr_pool_t *pool)
442 {
443     return APR_SUCCESS;
444 }
445 
446 #endif
447 
apr_proc_create(apr_proc_t * new,const char * progname,const char * const * args,const char * const * env,apr_procattr_t * attr,apr_pool_t * pool)448 APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new,
449                                           const char *progname,
450                                           const char * const *args,
451                                           const char * const *env,
452                                           apr_procattr_t *attr,
453                                           apr_pool_t *pool)
454 {
455     apr_status_t rv;
456     apr_size_t i;
457     const char *argv0;
458     char *cmdline;
459     char *pEnvBlock;
460     PROCESS_INFORMATION pi;
461     DWORD dwCreationFlags = 0;
462 
463     new->in = attr->parent_in;
464     new->out = attr->parent_out;
465     new->err = attr->parent_err;
466 
467     if (attr->detached) {
468         /* If we are creating ourselves detached, then we should hide the
469          * window we are starting in.  And we had better redefine our
470          * handles for STDIN, STDOUT, and STDERR. Do not set the
471          * detached attribute for Win9x. We have found that Win9x does
472          * not manage the stdio handles properly when running old 16
473          * bit executables if the detached attribute is set.
474          */
475         if (apr_os_level >= APR_WIN_NT) {
476             /*
477              * XXX DETACHED_PROCESS won't on Win9x at all; on NT/W2K
478              * 16 bit executables fail (MS KB: Q150956)
479              */
480             dwCreationFlags |= DETACHED_PROCESS;
481         }
482     }
483 
484     /* progname must be unquoted, in native format, as there are all sorts
485      * of bugs in the NT library loader code that fault when parsing '/'.
486      * XXX progname must be NULL if this is a 16 bit app running in WOW
487      */
488     if (progname[0] == '\"') {
489         progname = apr_pstrndup(pool, progname + 1, strlen(progname) - 2);
490     }
491 
492     if (attr->cmdtype == APR_PROGRAM || attr->cmdtype == APR_PROGRAM_ENV) {
493         char *fullpath = NULL;
494         if ((rv = apr_filepath_merge(&fullpath, attr->currdir, progname,
495                                      APR_FILEPATH_NATIVE, pool)) != APR_SUCCESS) {
496             if (attr->errfn) {
497                 attr->errfn(pool, rv,
498                             apr_pstrcat(pool, "filepath_merge failed.",
499                                         " currdir: ", attr->currdir,
500                                         " progname: ", progname, NULL));
501             }
502             return rv;
503         }
504         progname = fullpath;
505     }
506     else {
507         /* Do not fail if the path isn't parseable for APR_PROGRAM_PATH
508          * or APR_SHELLCMD.  We only invoke apr_filepath_merge (with no
509          * left hand side expression) in order to correct the path slash
510          * delimiters.  But the filename doesn't need to be in the CWD,
511          * nor does it need to be a filename at all (it could be a
512          * built-in shell command.)
513          */
514         char *fullpath = NULL;
515         if ((rv = apr_filepath_merge(&fullpath, "", progname,
516                                      APR_FILEPATH_NATIVE, pool)) == APR_SUCCESS) {
517             progname = fullpath;
518         }
519     }
520 
521     if (has_space(progname)) {
522         argv0 = apr_pstrcat(pool, "\"", progname, "\"", NULL);
523     }
524     else {
525         argv0 = progname;
526     }
527 
528     /* Handle the args, seperate from argv0 */
529     cmdline = "";
530     for (i = 1; args && args[i]; ++i) {
531         if (has_space(args[i]) || !args[i][0]) {
532             cmdline = apr_pstrcat(pool, cmdline, " \"", args[i], "\"", NULL);
533         }
534         else {
535             cmdline = apr_pstrcat(pool, cmdline, " ", args[i], NULL);
536         }
537     }
538 
539 #ifndef _WIN32_WCE
540     if (attr->cmdtype == APR_SHELLCMD || attr->cmdtype == APR_SHELLCMD_ENV) {
541         char *shellcmd = getenv("COMSPEC");
542         if (!shellcmd) {
543             if (attr->errfn) {
544                 attr->errfn(pool, APR_EINVAL, "COMSPEC envar is not set");
545             }
546             return APR_EINVAL;
547         }
548         if (shellcmd[0] == '"') {
549             progname = apr_pstrndup(pool, shellcmd + 1, strlen(shellcmd) - 2);
550         }
551         else {
552             progname = shellcmd;
553             if (has_space(shellcmd)) {
554                 shellcmd = apr_pstrcat(pool, "\"", shellcmd, "\"", NULL);
555             }
556         }
557         /* Command.com does not support a quoted command, while cmd.exe demands one.
558          */
559         i = strlen(progname);
560         if (i >= 11 && strcasecmp(progname + i - 11, "command.com") == 0) {
561             cmdline = apr_pstrcat(pool, shellcmd, " /C ", argv0, cmdline, NULL);
562         }
563         else {
564             cmdline = apr_pstrcat(pool, shellcmd, " /C \"", argv0, cmdline, "\"", NULL);
565         }
566     }
567     else
568 #endif
569     {
570 #if defined(_WIN32_WCE)
571         {
572 #else
573         /* Win32 is _different_ than unix.  While unix will find the given
574          * program since it's already chdir'ed, Win32 cannot since the parent
575          * attempts to open the program with it's own path.
576          * ###: This solution isn't much better - it may defeat path searching
577          * when the path search was desired.  Open to further discussion.
578          */
579         i = strlen(progname);
580         if (i >= 4 && (strcasecmp(progname + i - 4, ".bat") == 0
581                     || strcasecmp(progname + i - 4, ".cmd") == 0))
582         {
583             char *shellcmd = getenv("COMSPEC");
584             if (!shellcmd) {
585                 if (attr->errfn) {
586                     attr->errfn(pool, APR_EINVAL, "COMSPEC envar is not set");
587                 }
588                 return APR_EINVAL;
589             }
590             if (shellcmd[0] == '"') {
591                 progname = apr_pstrndup(pool, shellcmd + 1, strlen(shellcmd) - 2);
592             }
593             else {
594                 progname = shellcmd;
595                 if (has_space(shellcmd)) {
596                     shellcmd = apr_pstrcat(pool, "\"", shellcmd, "\"", NULL);
597                 }
598             }
599             i = strlen(progname);
600             if (i >= 11 && strcasecmp(progname + i - 11, "command.com") == 0) {
601                 /* XXX: Still insecure - need doubled-quotes on each individual
602                  * arg of cmdline.  Suspect we need to postpone cmdline parsing
603                  * until this moment in all four code paths, with some flags
604                  * to toggle 'which flavor' is needed.
605                  */
606                 cmdline = apr_pstrcat(pool, shellcmd, " /C ", argv0, cmdline, NULL);
607             }
608             else {
609                 /* We must protect the cmdline args from any interpolation - this
610                  * is not a shellcmd, and the source of argv[] is untrusted.
611                  * Notice we escape ALL the cmdline args, including the quotes
612                  * around the individual args themselves.  No sense in allowing
613                  * the shift-state to be toggled, and the application will
614                  * not see the caret escapes.
615                  */
616                 cmdline = apr_caret_escape_args(pool, cmdline);
617                 /*
618                  * Our app name must always be quoted so the quotes surrounding
619                  * the entire /c "command args" are unambigious.
620                  */
621                 if (*argv0 != '"') {
622                     cmdline = apr_pstrcat(pool, shellcmd, " /C \"\"", argv0, "\"", cmdline, "\"", NULL);
623                 }
624                 else {
625                     cmdline = apr_pstrcat(pool, shellcmd, " /C \"", argv0, cmdline, "\"", NULL);
626                 }
627             }
628         }
629         else {
630 #endif
631             /* A simple command we are directly invoking.  Do not pass
632              * the first arg to CreateProc() for APR_PROGRAM_PATH
633              * invocation, since it would need to be a literal and
634              * complete file path.  That is; "c:\bin\aprtest.exe"
635              * would succeed, but "c:\bin\aprtest" or "aprtest.exe"
636              * can fail.
637              */
638             cmdline = apr_pstrcat(pool, argv0, cmdline, NULL);
639 
640             if (attr->cmdtype == APR_PROGRAM_PATH) {
641                 progname = NULL;
642             }
643         }
644     }
645 
646     if (!env || attr->cmdtype == APR_PROGRAM_ENV ||
647         attr->cmdtype == APR_SHELLCMD_ENV) {
648         pEnvBlock = NULL;
649     }
650     else {
651         apr_size_t iEnvBlockLen;
652         /*
653          * Win32's CreateProcess call requires that the environment
654          * be passed in an environment block, a null terminated block of
655          * null terminated strings.
656          */
657         i = 0;
658         iEnvBlockLen = 1;
659         while (env[i]) {
660             iEnvBlockLen += strlen(env[i]) + 1;
661             i++;
662         }
663         if (!i)
664             ++iEnvBlockLen;
665 
666 #if APR_HAS_UNICODE_FS
667         IF_WIN_OS_IS_UNICODE
668         {
669             apr_wchar_t *pNext;
670             pEnvBlock = (char *)apr_palloc(pool, iEnvBlockLen * 2);
671             dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
672 
673             i = 0;
674             pNext = (apr_wchar_t*)pEnvBlock;
675             while (env[i]) {
676                 apr_size_t in = strlen(env[i]) + 1;
677                 if ((rv = apr_conv_utf8_to_ucs2(env[i], &in,
678                                                 pNext, &iEnvBlockLen))
679                         != APR_SUCCESS) {
680                     if (attr->errfn) {
681                         attr->errfn(pool, rv,
682                                     apr_pstrcat(pool,
683                                                 "utf8 to ucs2 conversion failed"
684                                                 " on this string: ", env[i], NULL));
685                     }
686                     return rv;
687                 }
688                 pNext = wcschr(pNext, L'\0') + 1;
689                 i++;
690             }
691 	    if (!i)
692                 *(pNext++) = L'\0';
693 	    *pNext = L'\0';
694         }
695 #endif /* APR_HAS_UNICODE_FS */
696 #if APR_HAS_ANSI_FS
697         ELSE_WIN_OS_IS_ANSI
698         {
699             char *pNext;
700             pEnvBlock = (char *)apr_palloc(pool, iEnvBlockLen);
701 
702             i = 0;
703             pNext = pEnvBlock;
704             while (env[i]) {
705                 strcpy(pNext, env[i]);
706                 pNext = strchr(pNext, '\0') + 1;
707                 i++;
708             }
709 	    if (!i)
710                 *(pNext++) = '\0';
711 	    *pNext = '\0';
712         }
713 #endif /* APR_HAS_ANSI_FS */
714     }
715 
716     new->invoked = cmdline;
717 
718 #if APR_HAS_UNICODE_FS
719     IF_WIN_OS_IS_UNICODE
720     {
721         STARTUPINFOW si;
722         DWORD stdin_reset = 0;
723         DWORD stdout_reset = 0;
724         DWORD stderr_reset = 0;
725         apr_wchar_t *wprg = NULL;
726         apr_wchar_t *wcmd = NULL;
727         apr_wchar_t *wcwd = NULL;
728 
729         if (progname) {
730             apr_size_t nprg = strlen(progname) + 1;
731             apr_size_t nwprg = nprg + 6;
732             wprg = apr_palloc(pool, nwprg * sizeof(wprg[0]));
733             if ((rv = apr_conv_utf8_to_ucs2(progname, &nprg, wprg, &nwprg))
734                    != APR_SUCCESS) {
735                 if (attr->errfn) {
736                     attr->errfn(pool, rv,
737                                 apr_pstrcat(pool,
738                                             "utf8 to ucs2 conversion failed"
739                                             " on progname: ", progname, NULL));
740                 }
741                 return rv;
742             }
743         }
744 
745         if (cmdline) {
746             apr_size_t ncmd = strlen(cmdline) + 1;
747             apr_size_t nwcmd = ncmd;
748             wcmd = apr_palloc(pool, nwcmd * sizeof(wcmd[0]));
749             if ((rv = apr_conv_utf8_to_ucs2(cmdline, &ncmd, wcmd, &nwcmd))
750                     != APR_SUCCESS) {
751                 if (attr->errfn) {
752                     attr->errfn(pool, rv,
753                                 apr_pstrcat(pool,
754                                             "utf8 to ucs2 conversion failed"
755                                             " on cmdline: ", cmdline, NULL));
756                 }
757                 return rv;
758             }
759         }
760 
761         if (attr->currdir)
762         {
763             apr_size_t ncwd = strlen(attr->currdir) + 1;
764             apr_size_t nwcwd = ncwd;
765             wcwd = apr_palloc(pool, ncwd * sizeof(wcwd[0]));
766             if ((rv = apr_conv_utf8_to_ucs2(attr->currdir, &ncwd,
767                                             wcwd, &nwcwd))
768                     != APR_SUCCESS) {
769                 if (attr->errfn) {
770                     attr->errfn(pool, rv,
771                                 apr_pstrcat(pool,
772                                             "utf8 to ucs2 conversion failed"
773                                             " on currdir: ", attr->currdir, NULL));
774                 }
775                 return rv;
776             }
777         }
778 
779         memset(&si, 0, sizeof(si));
780         si.cb = sizeof(si);
781 
782         if (attr->detached) {
783             si.dwFlags |= STARTF_USESHOWWINDOW;
784             si.wShowWindow = SW_HIDE;
785         }
786 
787 #ifndef _WIN32_WCE
788         /* LOCK CRITICAL SECTION
789          * before we begin to manipulate the inherited handles
790          */
791         EnterCriticalSection(&proc_lock);
792 
793         if ((attr->child_in && attr->child_in->filehand)
794             || (attr->child_out && attr->child_out->filehand)
795             || (attr->child_err && attr->child_err->filehand))
796         {
797             si.dwFlags |= STARTF_USESTDHANDLES;
798 
799             si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
800             if (attr->child_in && attr->child_in->filehand)
801             {
802                 if (GetHandleInformation(si.hStdInput,
803                                          &stdin_reset)
804                         && (stdin_reset &= HANDLE_FLAG_INHERIT))
805                     SetHandleInformation(si.hStdInput,
806                                          HANDLE_FLAG_INHERIT, 0);
807 
808                 if ( (si.hStdInput = attr->child_in->filehand)
809                                    != INVALID_HANDLE_VALUE )
810                     SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT,
811                                                        HANDLE_FLAG_INHERIT);
812             }
813 
814             si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
815             if (attr->child_out && attr->child_out->filehand)
816             {
817                 if (GetHandleInformation(si.hStdOutput,
818                                          &stdout_reset)
819                         && (stdout_reset &= HANDLE_FLAG_INHERIT))
820                     SetHandleInformation(si.hStdOutput,
821                                          HANDLE_FLAG_INHERIT, 0);
822 
823                 if ( (si.hStdOutput = attr->child_out->filehand)
824                                    != INVALID_HANDLE_VALUE )
825                     SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT,
826                                                         HANDLE_FLAG_INHERIT);
827             }
828 
829             si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
830             if (attr->child_err && attr->child_err->filehand)
831             {
832                 if (GetHandleInformation(si.hStdError,
833                                          &stderr_reset)
834                         && (stderr_reset &= HANDLE_FLAG_INHERIT))
835                     SetHandleInformation(si.hStdError,
836                                          HANDLE_FLAG_INHERIT, 0);
837 
838                 if ( (si.hStdError = attr->child_err->filehand)
839                                    != INVALID_HANDLE_VALUE )
840                     SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT,
841                                                        HANDLE_FLAG_INHERIT);
842             }
843         }
844         if (attr->user_token) {
845             /* XXX: for terminal services, handles can't be cannot be
846              * inherited across sessions.  This process must be created
847              * in our existing session.  lpDesktop assignment appears
848              * to be wrong according to these rules.
849              */
850             si.lpDesktop = L"Winsta0\\Default";
851             if (!ImpersonateLoggedOnUser(attr->user_token)) {
852             /* failed to impersonate the logged user */
853                 rv = apr_get_os_error();
854                 CloseHandle(attr->user_token);
855                 attr->user_token = NULL;
856                 LeaveCriticalSection(&proc_lock);
857                 return rv;
858             }
859             rv = CreateProcessAsUserW(attr->user_token,
860                                       wprg, wcmd,
861                                       attr->sa,
862                                       NULL,
863                                       TRUE,
864                                       dwCreationFlags,
865                                       pEnvBlock,
866                                       wcwd,
867                                       &si, &pi);
868 
869             RevertToSelf();
870         }
871         else {
872             rv = CreateProcessW(wprg, wcmd,        /* Executable & Command line */
873                                 NULL, NULL,        /* Proc & thread security attributes */
874                                 TRUE,              /* Inherit handles */
875                                 dwCreationFlags,   /* Creation flags */
876                                 pEnvBlock,         /* Environment block */
877                                 wcwd,              /* Current directory name */
878                                 &si, &pi);
879         }
880 
881         if ((attr->child_in && attr->child_in->filehand)
882             || (attr->child_out && attr->child_out->filehand)
883             || (attr->child_err && attr->child_err->filehand))
884         {
885             if (stdin_reset)
886                 SetHandleInformation(GetStdHandle(STD_INPUT_HANDLE),
887                                      stdin_reset, stdin_reset);
888 
889             if (stdout_reset)
890                 SetHandleInformation(GetStdHandle(STD_OUTPUT_HANDLE),
891                                      stdout_reset, stdout_reset);
892 
893             if (stderr_reset)
894                 SetHandleInformation(GetStdHandle(STD_ERROR_HANDLE),
895                                      stderr_reset, stderr_reset);
896         }
897         /* RELEASE CRITICAL SECTION
898          * The state of the inherited handles has been restored.
899          */
900         LeaveCriticalSection(&proc_lock);
901 
902 #else /* defined(_WIN32_WCE) */
903         rv = CreateProcessW(wprg, wcmd,        /* Executable & Command line */
904                             NULL, NULL,        /* Proc & thread security attributes */
905                             FALSE,             /* must be 0 */
906                             dwCreationFlags,   /* Creation flags */
907                             NULL,              /* Environment block must be NULL */
908                             NULL,              /* Current directory name must be NULL*/
909                             NULL,              /* STARTUPINFO not supported */
910                             &pi);
911 #endif
912     }
913 #endif /* APR_HAS_UNICODE_FS */
914 #if APR_HAS_ANSI_FS
915     ELSE_WIN_OS_IS_ANSI
916     {
917         STARTUPINFOA si;
918         memset(&si, 0, sizeof(si));
919         si.cb = sizeof(si);
920 
921         if (attr->detached) {
922             si.dwFlags |= STARTF_USESHOWWINDOW;
923             si.wShowWindow = SW_HIDE;
924         }
925 
926         if ((attr->child_in && attr->child_in->filehand)
927             || (attr->child_out && attr->child_out->filehand)
928             || (attr->child_err && attr->child_err->filehand))
929         {
930             si.dwFlags |= STARTF_USESTDHANDLES;
931 
932             si.hStdInput = (attr->child_in)
933                               ? attr->child_in->filehand
934                               : GetStdHandle(STD_INPUT_HANDLE);
935 
936             si.hStdOutput = (attr->child_out)
937                               ? attr->child_out->filehand
938                               : GetStdHandle(STD_OUTPUT_HANDLE);
939 
940             si.hStdError = (attr->child_err)
941                               ? attr->child_err->filehand
942                               : GetStdHandle(STD_ERROR_HANDLE);
943         }
944 
945         rv = CreateProcessA(progname, cmdline, /* Command line */
946                             NULL, NULL,        /* Proc & thread security attributes */
947                             TRUE,              /* Inherit handles */
948                             dwCreationFlags,   /* Creation flags */
949                             pEnvBlock,         /* Environment block */
950                             attr->currdir,     /* Current directory name */
951                             &si, &pi);
952     }
953 #endif /* APR_HAS_ANSI_FS */
954 
955     /* Check CreateProcess result
956      */
957     if (!rv)
958         return apr_get_os_error();
959 
960     /* XXX Orphaned handle warning - no fix due to broken apr_proc_t api.
961      */
962     new->hproc = pi.hProcess;
963     new->pid = pi.dwProcessId;
964 
965     if ((attr->child_in) && (attr->child_in != &no_file)) {
966         apr_file_close(attr->child_in);
967     }
968     if ((attr->child_out) && (attr->child_out != &no_file)) {
969         apr_file_close(attr->child_out);
970     }
971     if ((attr->child_err) && (attr->child_err != &no_file)) {
972         apr_file_close(attr->child_err);
973     }
974     CloseHandle(pi.hThread);
975 
976     return APR_SUCCESS;
977 }
978 
979 static apr_exit_why_e why_from_exit_code(DWORD exit) {
980     /* See WinNT.h STATUS_ACCESS_VIOLATION and family for how
981      * this class of failures was determined
982      */
983     if (((exit & 0xC0000000) == 0xC0000000)
984                     && !(exit & 0x3FFF0000))
985         return APR_PROC_SIGNAL;
986     else
987         return APR_PROC_EXIT;
988 
989     /* ### No way to tell if Dr Watson grabbed a core, AFAICT. */
990 }
991 
992 APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc,
993                                                   int *exitcode,
994                                                   apr_exit_why_e *exitwhy,
995                                                   apr_wait_how_e waithow,
996                                                   apr_pool_t *p)
997 {
998 #if APR_HAS_UNICODE_FS
999 #ifndef _WIN32_WCE
1000     IF_WIN_OS_IS_UNICODE
1001     {
1002         DWORD  dwId    = GetCurrentProcessId();
1003         DWORD  i;
1004         DWORD  nChilds = 0;
1005         DWORD  nActive = 0;
1006         HANDLE ps32;
1007         PROCESSENTRY32W pe32;
1008         BOOL   bHasMore = FALSE;
1009         DWORD  dwFlags  = PROCESS_QUERY_INFORMATION;
1010         apr_status_t rv = APR_EGENERAL;
1011 
1012         if (waithow == APR_WAIT)
1013             dwFlags |= SYNCHRONIZE;
1014         if (!(ps32 = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0))) {
1015             return apr_get_os_error();
1016         }
1017         pe32.dwSize = sizeof(PROCESSENTRY32W);
1018         if (!Process32FirstW(ps32, &pe32)) {
1019             if (GetLastError() == ERROR_NO_MORE_FILES)
1020                 return APR_EOF;
1021             else
1022                 return apr_get_os_error();
1023         }
1024         do {
1025             DWORD  dwRetval = 0;
1026             DWORD  nHandles = 0;
1027             HANDLE hProcess = NULL;
1028             HANDLE pHandles[MAXIMUM_WAIT_OBJECTS];
1029             do {
1030                 if (pe32.th32ParentProcessID == dwId) {
1031                     nChilds++;
1032                     if ((hProcess = OpenProcess(dwFlags, FALSE,
1033                                                 pe32.th32ProcessID)) != NULL) {
1034                         if (GetExitCodeProcess(hProcess, &dwRetval)) {
1035                             if (dwRetval == STILL_ACTIVE) {
1036                                 nActive++;
1037                                 if (waithow == APR_WAIT)
1038                                     pHandles[nHandles++] = hProcess;
1039                                 else
1040                                     CloseHandle(hProcess);
1041                             }
1042                             else {
1043                                 /* Process has exited.
1044                                  * No need to wait for its termination.
1045                                  */
1046                                 CloseHandle(hProcess);
1047                                 if (exitcode)
1048                                     *exitcode = dwRetval;
1049                                 if (exitwhy)
1050                                     *exitwhy  = why_from_exit_code(dwRetval);
1051                                 proc->pid = pe32.th32ProcessID;
1052                             }
1053                         }
1054                         else {
1055                             /* Unexpected error code.
1056                              * Cleanup and return;
1057                              */
1058                             rv = apr_get_os_error();
1059                             CloseHandle(hProcess);
1060                             for (i = 0; i < nHandles; i++)
1061                                 CloseHandle(pHandles[i]);
1062                             return rv;
1063                         }
1064                     }
1065                     else {
1066                         /* This is our child, so it shouldn't happen
1067                          * that we cannot open our child's process handle.
1068                          * However if the child process increased the
1069                          * security token it might fail.
1070                          */
1071                     }
1072                 }
1073             } while ((bHasMore = Process32NextW(ps32, &pe32)) &&
1074                      nHandles < MAXIMUM_WAIT_OBJECTS);
1075             if (nHandles) {
1076                 /* Wait for all collected processes to finish */
1077                 DWORD waitStatus = WaitForMultipleObjects(nHandles, pHandles,
1078                                                           TRUE, INFINITE);
1079                 for (i = 0; i < nHandles; i++)
1080                     CloseHandle(pHandles[i]);
1081                 if (waitStatus == WAIT_OBJECT_0) {
1082                     /* Decrease active count by the number of awaited
1083                      * processes.
1084                      */
1085                     nActive -= nHandles;
1086                 }
1087                 else {
1088                     /* Broken from the infinite loop */
1089                     break;
1090                 }
1091             }
1092         } while (bHasMore);
1093         CloseHandle(ps32);
1094         if (waithow != APR_WAIT) {
1095             if (nChilds && nChilds == nActive) {
1096                 /* All child processes are running */
1097                 rv = APR_CHILD_NOTDONE;
1098                 proc->pid = -1;
1099             }
1100             else {
1101                 /* proc->pid contains the pid of the
1102                  * exited processes
1103                  */
1104                 rv = APR_CHILD_DONE;
1105             }
1106         }
1107         if (nActive == 0) {
1108             rv = APR_CHILD_DONE;
1109             proc->pid = -1;
1110         }
1111         return rv;
1112     }
1113 #endif /* _WIN32_WCE */
1114 #endif /* APR_HAS_UNICODE_FS */
1115     return APR_ENOTIMPL;
1116 }
1117 
1118 APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc,
1119                                         int *exitcode, apr_exit_why_e *exitwhy,
1120                                         apr_wait_how_e waithow)
1121 {
1122     DWORD stat;
1123     DWORD time;
1124 
1125     if (waithow == APR_WAIT)
1126         time = INFINITE;
1127     else
1128         time = 0;
1129 
1130     if ((stat = WaitForSingleObject(proc->hproc, time)) == WAIT_OBJECT_0) {
1131         if (GetExitCodeProcess(proc->hproc, &stat)) {
1132             if (exitcode)
1133                 *exitcode = stat;
1134             if (exitwhy)
1135                 *exitwhy = why_from_exit_code(stat);
1136             CloseHandle(proc->hproc);
1137             proc->hproc = NULL;
1138             return APR_CHILD_DONE;
1139         }
1140     }
1141     else if (stat == WAIT_TIMEOUT) {
1142         return APR_CHILD_NOTDONE;
1143     }
1144     return apr_get_os_error();
1145 }
1146 
1147 APR_DECLARE(apr_status_t) apr_proc_detach(int daemonize)
1148 {
1149     return APR_ENOTIMPL;
1150 }
1151