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