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