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 */
38static 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 */
68APR_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
77APR_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
127APR_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
153APR_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
179APR_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
205APR_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
215APR_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
222APR_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
230static 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
240APR_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
337APR_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
344static 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
355static 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
386APR_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
393APR_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
400APR_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 */
412static CRITICAL_SECTION proc_lock;
413
414static 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 */
426apr_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
441apr_status_t apr_threadproc_init(apr_pool_t *pool)
442{
443    return APR_SUCCESS;
444}
445
446#endif
447
448APR_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 redfine 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                return rv;
857            }
858            rv = CreateProcessAsUserW(attr->user_token,
859                                      wprg, wcmd,
860                                      attr->sa,
861                                      NULL,
862                                      TRUE,
863                                      dwCreationFlags,
864                                      pEnvBlock,
865                                      wcwd,
866                                      &si, &pi);
867
868            RevertToSelf();
869        }
870        else {
871            rv = CreateProcessW(wprg, wcmd,        /* Executable & Command line */
872                                NULL, NULL,        /* Proc & thread security attributes */
873                                TRUE,              /* Inherit handles */
874                                dwCreationFlags,   /* Creation flags */
875                                pEnvBlock,         /* Environment block */
876                                wcwd,              /* Current directory name */
877                                &si, &pi);
878        }
879
880        if ((attr->child_in && attr->child_in->filehand)
881            || (attr->child_out && attr->child_out->filehand)
882            || (attr->child_err && attr->child_err->filehand))
883        {
884            if (stdin_reset)
885                SetHandleInformation(GetStdHandle(STD_INPUT_HANDLE),
886                                     stdin_reset, stdin_reset);
887
888            if (stdout_reset)
889                SetHandleInformation(GetStdHandle(STD_OUTPUT_HANDLE),
890                                     stdout_reset, stdout_reset);
891
892            if (stderr_reset)
893                SetHandleInformation(GetStdHandle(STD_ERROR_HANDLE),
894                                     stderr_reset, stderr_reset);
895        }
896        /* RELEASE CRITICAL SECTION
897         * The state of the inherited handles has been restored.
898         */
899        LeaveCriticalSection(&proc_lock);
900
901#else /* defined(_WIN32_WCE) */
902        rv = CreateProcessW(wprg, wcmd,        /* Executable & Command line */
903                            NULL, NULL,        /* Proc & thread security attributes */
904                            FALSE,             /* must be 0 */
905                            dwCreationFlags,   /* Creation flags */
906                            NULL,              /* Environment block must be NULL */
907                            NULL,              /* Current directory name must be NULL*/
908                            NULL,              /* STARTUPINFO not supported */
909                            &pi);
910#endif
911    }
912#endif /* APR_HAS_UNICODE_FS */
913#if APR_HAS_ANSI_FS
914    ELSE_WIN_OS_IS_ANSI
915    {
916        STARTUPINFOA si;
917        memset(&si, 0, sizeof(si));
918        si.cb = sizeof(si);
919
920        if (attr->detached) {
921            si.dwFlags |= STARTF_USESHOWWINDOW;
922            si.wShowWindow = SW_HIDE;
923        }
924
925        if ((attr->child_in && attr->child_in->filehand)
926            || (attr->child_out && attr->child_out->filehand)
927            || (attr->child_err && attr->child_err->filehand))
928        {
929            si.dwFlags |= STARTF_USESTDHANDLES;
930
931            si.hStdInput = (attr->child_in)
932                              ? attr->child_in->filehand
933                              : GetStdHandle(STD_INPUT_HANDLE);
934
935            si.hStdOutput = (attr->child_out)
936                              ? attr->child_out->filehand
937                              : GetStdHandle(STD_OUTPUT_HANDLE);
938
939            si.hStdError = (attr->child_err)
940                              ? attr->child_err->filehand
941                              : GetStdHandle(STD_ERROR_HANDLE);
942        }
943
944        rv = CreateProcessA(progname, cmdline, /* Command line */
945                            NULL, NULL,        /* Proc & thread security attributes */
946                            TRUE,              /* Inherit handles */
947                            dwCreationFlags,   /* Creation flags */
948                            pEnvBlock,         /* Environment block */
949                            attr->currdir,     /* Current directory name */
950                            &si, &pi);
951    }
952#endif /* APR_HAS_ANSI_FS */
953
954    /* Check CreateProcess result
955     */
956    if (!rv)
957        return apr_get_os_error();
958
959    /* XXX Orphaned handle warning - no fix due to broken apr_proc_t api.
960     */
961    new->hproc = pi.hProcess;
962    new->pid = pi.dwProcessId;
963
964    if ((attr->child_in) && (attr->child_in != &no_file)) {
965        apr_file_close(attr->child_in);
966    }
967    if ((attr->child_out) && (attr->child_out != &no_file)) {
968        apr_file_close(attr->child_out);
969    }
970    if ((attr->child_err) && (attr->child_err != &no_file)) {
971        apr_file_close(attr->child_err);
972    }
973    CloseHandle(pi.hThread);
974
975    return APR_SUCCESS;
976}
977
978static apr_exit_why_e why_from_exit_code(DWORD exit) {
979    /* See WinNT.h STATUS_ACCESS_VIOLATION and family for how
980     * this class of failures was determined
981     */
982    if (((exit & 0xC0000000) == 0xC0000000)
983                    && !(exit & 0x3FFF0000))
984        return APR_PROC_SIGNAL;
985    else
986        return APR_PROC_EXIT;
987
988    /* ### No way to tell if Dr Watson grabbed a core, AFAICT. */
989}
990
991APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc,
992                                                  int *exitcode,
993                                                  apr_exit_why_e *exitwhy,
994                                                  apr_wait_how_e waithow,
995                                                  apr_pool_t *p)
996{
997#if APR_HAS_UNICODE_FS
998#ifndef _WIN32_WCE
999    IF_WIN_OS_IS_UNICODE
1000    {
1001        DWORD  dwId    = GetCurrentProcessId();
1002        DWORD  i;
1003        DWORD  nChilds = 0;
1004        DWORD  nActive = 0;
1005        HANDLE ps32;
1006        PROCESSENTRY32W pe32;
1007        BOOL   bHasMore = FALSE;
1008        DWORD  dwFlags  = PROCESS_QUERY_INFORMATION;
1009        apr_status_t rv = APR_EGENERAL;
1010
1011        if (waithow == APR_WAIT)
1012            dwFlags |= SYNCHRONIZE;
1013        if (!(ps32 = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0))) {
1014            return apr_get_os_error();
1015        }
1016        pe32.dwSize = sizeof(PROCESSENTRY32W);
1017        if (!Process32FirstW(ps32, &pe32)) {
1018            if (GetLastError() == ERROR_NO_MORE_FILES)
1019                return APR_EOF;
1020            else
1021                return apr_get_os_error();
1022        }
1023        do {
1024            DWORD  dwRetval = 0;
1025            DWORD  nHandles = 0;
1026            HANDLE hProcess = NULL;
1027            HANDLE pHandles[MAXIMUM_WAIT_OBJECTS];
1028            do {
1029                if (pe32.th32ParentProcessID == dwId) {
1030                    nChilds++;
1031                    if ((hProcess = OpenProcess(dwFlags, FALSE,
1032                                                pe32.th32ProcessID)) != NULL) {
1033                        if (GetExitCodeProcess(hProcess, &dwRetval)) {
1034                            if (dwRetval == STILL_ACTIVE) {
1035                                nActive++;
1036                                if (waithow == APR_WAIT)
1037                                    pHandles[nHandles++] = hProcess;
1038                                else
1039                                    CloseHandle(hProcess);
1040                            }
1041                            else {
1042                                /* Process has exited.
1043                                 * No need to wait for its termination.
1044                                 */
1045                                CloseHandle(hProcess);
1046                                if (exitcode)
1047                                    *exitcode = dwRetval;
1048                                if (exitwhy)
1049                                    *exitwhy  = why_from_exit_code(dwRetval);
1050                                proc->pid = pe32.th32ProcessID;
1051                            }
1052                        }
1053                        else {
1054                            /* Unexpected error code.
1055                             * Cleanup and return;
1056                             */
1057                            rv = apr_get_os_error();
1058                            CloseHandle(hProcess);
1059                            for (i = 0; i < nHandles; i++)
1060                                CloseHandle(pHandles[i]);
1061                            return rv;
1062                        }
1063                    }
1064                    else {
1065                        /* This is our child, so it shouldn't happen
1066                         * that we cannot open our child's process handle.
1067                         * However if the child process increased the
1068                         * security token it might fail.
1069                         */
1070                    }
1071                }
1072            } while ((bHasMore = Process32NextW(ps32, &pe32)) &&
1073                     nHandles < MAXIMUM_WAIT_OBJECTS);
1074            if (nHandles) {
1075                /* Wait for all collected processes to finish */
1076                DWORD waitStatus = WaitForMultipleObjects(nHandles, pHandles,
1077                                                          TRUE, INFINITE);
1078                for (i = 0; i < nHandles; i++)
1079                    CloseHandle(pHandles[i]);
1080                if (waitStatus == WAIT_OBJECT_0) {
1081                    /* Decrease active count by the number of awaited
1082                     * processes.
1083                     */
1084                    nActive -= nHandles;
1085                }
1086                else {
1087                    /* Broken from the infinite loop */
1088                    break;
1089                }
1090            }
1091        } while (bHasMore);
1092        CloseHandle(ps32);
1093        if (waithow != APR_WAIT) {
1094            if (nChilds && nChilds == nActive) {
1095                /* All child processes are running */
1096                rv = APR_CHILD_NOTDONE;
1097                proc->pid = -1;
1098            }
1099            else {
1100                /* proc->pid contains the pid of the
1101                 * exited processes
1102                 */
1103                rv = APR_CHILD_DONE;
1104            }
1105        }
1106        if (nActive == 0) {
1107            rv = APR_CHILD_DONE;
1108            proc->pid = -1;
1109        }
1110        return rv;
1111    }
1112#endif /* _WIN32_WCE */
1113#endif /* APR_HAS_UNICODE_FS */
1114    return APR_ENOTIMPL;
1115}
1116
1117APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc,
1118                                        int *exitcode, apr_exit_why_e *exitwhy,
1119                                        apr_wait_how_e waithow)
1120{
1121    DWORD stat;
1122    DWORD time;
1123
1124    if (waithow == APR_WAIT)
1125        time = INFINITE;
1126    else
1127        time = 0;
1128
1129    if ((stat = WaitForSingleObject(proc->hproc, time)) == WAIT_OBJECT_0) {
1130        if (GetExitCodeProcess(proc->hproc, &stat)) {
1131            if (exitcode)
1132                *exitcode = stat;
1133            if (exitwhy)
1134                *exitwhy = why_from_exit_code(stat);
1135            CloseHandle(proc->hproc);
1136            proc->hproc = NULL;
1137            return APR_CHILD_DONE;
1138        }
1139    }
1140    else if (stat == WAIT_TIMEOUT) {
1141        return APR_CHILD_NOTDONE;
1142    }
1143    return apr_get_os_error();
1144}
1145
1146APR_DECLARE(apr_status_t) apr_proc_detach(int daemonize)
1147{
1148    return APR_ENOTIMPL;
1149}
1150