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#define INCL_DOS 18#define INCL_DOSERRORS 19 20#include "apr_arch_threadproc.h" 21#include "apr_arch_file_io.h" 22#include "apr_private.h" 23#include "apr_thread_proc.h" 24#include "apr_file_io.h" 25#include "apr_general.h" 26#include "apr_lib.h" 27#include "apr_portable.h" 28#include "apr_strings.h" 29#include "apr_signal.h" 30#include <signal.h> 31#include <string.h> 32#include <sys/wait.h> 33#include <unistd.h> 34#include <process.h> 35#include <stdlib.h> 36 37/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE 38 * requested for a specific child handle; 39 */ 40static apr_file_t no_file = { NULL, -1, }; 41 42APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new, apr_pool_t *pool) 43{ 44 (*new) = (apr_procattr_t *)apr_palloc(pool, 45 sizeof(apr_procattr_t)); 46 47 if ((*new) == NULL) { 48 return APR_ENOMEM; 49 } 50 (*new)->pool = pool; 51 (*new)->parent_in = NULL; 52 (*new)->child_in = NULL; 53 (*new)->parent_out = NULL; 54 (*new)->child_out = NULL; 55 (*new)->parent_err = NULL; 56 (*new)->child_err = NULL; 57 (*new)->currdir = NULL; 58 (*new)->cmdtype = APR_PROGRAM; 59 (*new)->detached = FALSE; 60 return APR_SUCCESS; 61} 62 63APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, 64 apr_int32_t in, 65 apr_int32_t out, 66 apr_int32_t err) 67{ 68 apr_status_t rv; 69 70 if ((in != APR_NO_PIPE) && (in != APR_NO_FILE)) { 71 /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while 72 * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose 73 * the CHILD/PARENT blocking flags for the stdin pipe. 74 * stdout/stderr map to the correct mode by default. 75 */ 76 if (in == APR_CHILD_BLOCK) 77 in = APR_READ_BLOCK; 78 else if (in == APR_PARENT_BLOCK) 79 in = APR_WRITE_BLOCK; 80 81 if ((rv = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in, 82 in, attr->pool)) == APR_SUCCESS) 83 rv = apr_file_inherit_unset(attr->parent_in); 84 if (rv != APR_SUCCESS) 85 return rv; 86 } 87 else if (in == APR_NO_FILE) 88 attr->child_in = &no_file; 89 90 if ((out != APR_NO_PIPE) && (out != APR_NO_FILE)) { 91 if ((rv = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out, 92 out, attr->pool)) == APR_SUCCESS) 93 rv = apr_file_inherit_unset(attr->parent_out); 94 if (rv != APR_SUCCESS) 95 return rv; 96 } 97 else if (out == APR_NO_FILE) 98 attr->child_out = &no_file; 99 100 if ((err != APR_NO_PIPE) && (err != APR_NO_FILE)) { 101 if ((rv = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err, 102 err, attr->pool)) == APR_SUCCESS) 103 rv = apr_file_inherit_unset(attr->parent_err); 104 if (rv != APR_SUCCESS) 105 return rv; 106 } 107 else if (err == APR_NO_FILE) 108 attr->child_err = &no_file; 109 110 return APR_SUCCESS; 111} 112 113APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, apr_file_t *child_in, 114 apr_file_t *parent_in) 115{ 116 apr_status_t rv; 117 118 if (attr->child_in == NULL && attr->parent_in == NULL 119 && child_in == NULL && parent_in == NULL) 120 if ((rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in, 121 attr->pool)) == APR_SUCCESS) 122 rv = apr_file_inherit_unset(attr->parent_in); 123 124 if (child_in != NULL && rv == APR_SUCCESS) { 125 if (attr->child_in && (attr->child_in->filedes != -1)) 126 rv = apr_file_dup2(attr->child_in, child_in, attr->pool); 127 else { 128 attr->child_in = NULL; 129 if ((rv = apr_file_dup(&attr->child_in, child_in, attr->pool)) 130 == APR_SUCCESS) 131 rv = apr_file_inherit_set(attr->child_in); 132 } 133 } 134 135 if (parent_in != NULL && rv == APR_SUCCESS) { 136 rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool); 137 } 138 139 return rv; 140} 141 142APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, apr_file_t *child_out, 143 apr_file_t *parent_out) 144{ 145 apr_status_t rv; 146 147 if (attr->child_out == NULL && attr->parent_out == NULL 148 && child_out == NULL && parent_out == NULL) 149 if ((rv = apr_file_pipe_create(&attr->parent_out, &attr->child_out, 150 attr->pool)) == APR_SUCCESS) 151 rv = apr_file_inherit_unset(attr->parent_out); 152 153 if (child_out != NULL && rv == APR_SUCCESS) { 154 if (attr->child_out && (attr->child_out->filedes != -1)) 155 rv = apr_file_dup2(attr->child_out, child_out, attr->pool); 156 else { 157 attr->child_out = NULL; 158 if ((rv = apr_file_dup(&attr->child_out, child_out, attr->pool)) 159 == APR_SUCCESS) 160 rv = apr_file_inherit_set(attr->child_out); 161 } 162 } 163 164 if (parent_out != NULL && rv == APR_SUCCESS) { 165 rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool); 166 } 167 168 return rv; 169} 170 171APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, apr_file_t *child_err, 172 apr_file_t *parent_err) 173{ 174 apr_status_t rv; 175 176 if (attr->child_err == NULL && attr->parent_err == NULL 177 && child_err == NULL && parent_err == NULL) 178 if ((rv = apr_file_pipe_create(&attr->parent_err, &attr->child_err, 179 attr->pool)) == APR_SUCCESS) 180 rv = apr_file_inherit_unset(attr->parent_err); 181 182 if (child_err != NULL && rv == APR_SUCCESS) { 183 if (attr->child_err && (attr->child_err->filedes != -1)) 184 rv = apr_file_dup2(attr->child_err, child_err, attr->pool); 185 else { 186 attr->child_err = NULL; 187 if ((rv = apr_file_dup(&attr->child_err, child_err, attr->pool)) 188 == APR_SUCCESS) 189 rv = apr_file_inherit_set(attr->child_err); 190 } 191 } 192 193 if (parent_err != NULL && rv == APR_SUCCESS) { 194 rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool); 195 } 196 197 return rv; 198} 199 200APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, const char *dir) 201{ 202 attr->currdir = apr_pstrdup(attr->pool, dir); 203 if (attr->currdir) { 204 return APR_SUCCESS; 205 } 206 return APR_ENOMEM; 207} 208 209APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr, 210 apr_cmdtype_e cmd) 211{ 212 attr->cmdtype = cmd; 213 return APR_SUCCESS; 214} 215 216APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, apr_int32_t detach) 217{ 218 attr->detached = detach; 219 return APR_SUCCESS; 220} 221 222APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool) 223{ 224 int pid; 225 226 if ((pid = fork()) < 0) { 227 return errno; 228 } 229 else if (pid == 0) { 230 proc->pid = pid; 231 proc->in = NULL; 232 proc->out = NULL; 233 proc->err = NULL; 234 return APR_INCHILD; 235 } 236 proc->pid = pid; 237 proc->in = NULL; 238 proc->out = NULL; 239 proc->err = NULL; 240 return APR_INPARENT; 241} 242 243 244 245/* quotes in the string are doubled up. 246 * Used to escape quotes in args passed to OS/2's cmd.exe 247 */ 248static char *double_quotes(apr_pool_t *pool, const char *str) 249{ 250 int num_quotes = 0; 251 int len = 0; 252 char *quote_doubled_str, *dest; 253 254 while (str[len]) { 255 num_quotes += str[len++] == '\"'; 256 } 257 258 quote_doubled_str = apr_palloc(pool, len + num_quotes + 1); 259 dest = quote_doubled_str; 260 261 while (*str) { 262 if (*str == '\"') 263 *(dest++) = '\"'; 264 *(dest++) = *(str++); 265 } 266 267 *dest = 0; 268 return quote_doubled_str; 269} 270 271 272 273APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr, 274 apr_child_errfn_t *errfn) 275{ 276 /* won't ever be called on this platform, so don't save the function pointer */ 277 return APR_SUCCESS; 278} 279 280 281 282APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr, 283 apr_int32_t chk) 284{ 285 /* won't ever be used on this platform, so don't save the flag */ 286 return APR_SUCCESS; 287} 288 289APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr, 290 apr_int32_t addrspace) 291{ 292 /* won't ever be used on this platform, so don't save the flag */ 293 return APR_SUCCESS; 294} 295 296 297 298APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *proc, const char *progname, 299 const char * const *args, 300 const char * const *env, 301 apr_procattr_t *attr, apr_pool_t *pool) 302{ 303 int i, arg, numargs, cmdlen; 304 apr_status_t status; 305 const char **newargs; 306 char savedir[300]; 307 HFILE save_in, save_out, save_err, dup; 308 int criticalsection = FALSE; 309 char *extension, *newprogname, *extra_arg = NULL, *cmdline, *cmdline_pos; 310 char interpreter[1024]; 311 char error_object[260]; 312 apr_file_t *progfile; 313 int env_len, e; 314 char *env_block, *env_block_pos; 315 RESULTCODES rescodes; 316 317 proc->in = attr->parent_in; 318 proc->err = attr->parent_err; 319 proc->out = attr->parent_out; 320 321 /* Prevent other threads from running while these process-wide resources are modified */ 322 if (attr->child_in || attr->child_out || attr->child_err || attr->currdir) { 323 criticalsection = TRUE; 324 DosEnterCritSec(); 325 } 326 327 if (attr->child_in) { 328 save_in = -1; 329 DosDupHandle(STDIN_FILENO, &save_in); 330 dup = STDIN_FILENO; 331 if (attr->child_in->filedes == -1) 332 DosClose(dup); 333 else 334 DosDupHandle(attr->child_in->filedes, &dup); 335 } 336 337 if (attr->child_out) { 338 save_out = -1; 339 DosDupHandle(STDOUT_FILENO, &save_out); 340 dup = STDOUT_FILENO; 341 if (attr->child_out->filedes == -1) 342 DosClose(dup); 343 else 344 DosDupHandle(attr->child_out->filedes, &dup); 345 } 346 347 if (attr->child_err) { 348 save_err = -1; 349 DosDupHandle(STDERR_FILENO, &save_err); 350 dup = STDERR_FILENO; 351 if (attr->child_err->filedes == -1) 352 DosClose(dup); 353 else 354 DosDupHandle(attr->child_err->filedes, &dup); 355 } 356 357 apr_signal(SIGCHLD, SIG_DFL); /*not sure if this is needed or not */ 358 359 if (attr->currdir != NULL) { 360 _getcwd2(savedir, sizeof(savedir)); 361 362 if (_chdir2(attr->currdir) < 0) { 363 if (criticalsection) 364 DosExitCritSec(); 365 return errno; 366 } 367 } 368 369 interpreter[0] = 0; 370 extension = strrchr(progname, '.'); 371 372 if (extension == NULL || strchr(extension, '/') || strchr(extension, '\\')) 373 extension = ""; 374 375 /* ### how to handle APR_PROGRAM_ENV and APR_PROGRAM_PATH? */ 376 377 if (attr->cmdtype == APR_SHELLCMD || 378 attr->cmdtype == APR_SHELLCMD_ENV || 379 strcasecmp(extension, ".cmd") == 0) { 380 strcpy(interpreter, "#!" SHELL_PATH); 381 extra_arg = "/C"; 382 } else if (stricmp(extension, ".exe") != 0) { 383 status = apr_file_open(&progfile, progname, APR_READ|APR_BUFFERED, 0, pool); 384 385 if (status != APR_SUCCESS && APR_STATUS_IS_ENOENT(status)) { 386 progname = apr_pstrcat(pool, progname, ".exe", NULL); 387 } 388 389 if (status == APR_SUCCESS) { 390 status = apr_file_gets(interpreter, sizeof(interpreter), progfile); 391 392 if (status == APR_SUCCESS) { 393 if (interpreter[0] == '#' && interpreter[1] == '!') { 394 /* delete CR/LF & any other whitespace off the end */ 395 int end = strlen(interpreter) - 1; 396 397 while (end >= 0 && apr_isspace(interpreter[end])) { 398 interpreter[end] = '\0'; 399 end--; 400 } 401 402 if (interpreter[2] != '/' && interpreter[2] != '\\' && interpreter[3] != ':') { 403 char buffer[300]; 404 405 if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { 406 strcpy(interpreter+2, buffer); 407 } else { 408 strcat(interpreter, ".exe"); 409 if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { 410 strcpy(interpreter+2, buffer); 411 } 412 } 413 } 414 } else { 415 interpreter[0] = 0; 416 } 417 } 418 419 apr_file_close(progfile); 420 } 421 } 422 423 i = 0; 424 425 while (args && args[i]) { 426 i++; 427 } 428 429 newargs = (const char **)apr_palloc(pool, sizeof (char *) * (i + 4)); 430 numargs = 0; 431 432 if (interpreter[0]) 433 newargs[numargs++] = interpreter + 2; 434 if (extra_arg) 435 newargs[numargs++] = "/c"; 436 437 newargs[numargs++] = newprogname = apr_pstrdup(pool, progname); 438 arg = 1; 439 440 while (args && args[arg]) { 441 newargs[numargs++] = args[arg++]; 442 } 443 444 newargs[numargs] = NULL; 445 446 for (i=0; newprogname[i]; i++) 447 if (newprogname[i] == '/') 448 newprogname[i] = '\\'; 449 450 cmdlen = 0; 451 452 for (i=0; i<numargs; i++) 453 cmdlen += strlen(newargs[i]) + 3; 454 455 cmdline = apr_palloc(pool, cmdlen + 2); 456 cmdline_pos = cmdline; 457 458 for (i=0; i<numargs; i++) { 459 const char *a = newargs[i]; 460 461 if (strpbrk(a, "&|<>\" ")) 462 a = apr_pstrcat(pool, "\"", double_quotes(pool, a), "\"", NULL); 463 464 if (i) 465 *(cmdline_pos++) = ' '; 466 467 strcpy(cmdline_pos, a); 468 cmdline_pos += strlen(cmdline_pos); 469 } 470 471 *(++cmdline_pos) = 0; /* Add required second terminator */ 472 cmdline_pos = strchr(cmdline, ' '); 473 474 if (cmdline_pos) { 475 *cmdline_pos = 0; 476 cmdline_pos++; 477 } 478 479 /* Create environment block from list of envariables */ 480 if (env) { 481 for (env_len=1, e=0; env[e]; e++) 482 env_len += strlen(env[e]) + 1; 483 484 env_block = apr_palloc(pool, env_len); 485 env_block_pos = env_block; 486 487 for (e=0; env[e]; e++) { 488 strcpy(env_block_pos, env[e]); 489 env_block_pos += strlen(env_block_pos) + 1; 490 } 491 492 *env_block_pos = 0; /* environment block is terminated by a double null */ 493 } else 494 env_block = NULL; 495 496 status = DosExecPgm(error_object, sizeof(error_object), 497 attr->detached ? EXEC_BACKGROUND : EXEC_ASYNCRESULT, 498 cmdline, env_block, &rescodes, cmdline); 499 500 proc->pid = rescodes.codeTerminate; 501 502 if (attr->currdir != NULL) { 503 chdir(savedir); 504 } 505 506 if (attr->child_in) { 507 if (attr->child_in->filedes != -1) { 508 apr_file_close(attr->child_in); 509 } 510 511 dup = STDIN_FILENO; 512 DosDupHandle(save_in, &dup); 513 DosClose(save_in); 514 } 515 516 if (attr->child_out) { 517 if (attr->child_out->filedes != -1) { 518 apr_file_close(attr->child_out); 519 } 520 521 dup = STDOUT_FILENO; 522 DosDupHandle(save_out, &dup); 523 DosClose(save_out); 524 } 525 526 if (attr->child_err) { 527 if (attr->child_err->filedes != -1) { 528 apr_file_close(attr->child_err); 529 } 530 531 dup = STDERR_FILENO; 532 DosDupHandle(save_err, &dup); 533 DosClose(save_err); 534 } 535 536 if (criticalsection) 537 DosExitCritSec(); 538 539 return status; 540} 541 542 543 544static void proces_result_codes(RESULTCODES codes, 545 int *exitcode, 546 apr_exit_why_e *exitwhy) 547{ 548 int result = 0; 549 apr_exit_why_e why = APR_PROC_EXIT; 550 551 switch (codes.codeTerminate) { 552 case TC_EXIT: /* Normal exit */ 553 why = APR_PROC_EXIT; 554 result = codes.codeResult; 555 break; 556 557 case TC_HARDERROR: /* Hard error halt */ 558 why = APR_PROC_SIGNAL; 559 result = SIGSYS; 560 break; 561 562 case TC_KILLPROCESS: /* Was killed by a DosKillProcess() */ 563 why = APR_PROC_SIGNAL; 564 result = SIGKILL; 565 break; 566 567 case TC_TRAP: /* TRAP in 16 bit code */ 568 case TC_EXCEPTION: /* Threw an exception (32 bit code) */ 569 why = APR_PROC_SIGNAL; 570 571 switch (codes.codeResult | XCPT_FATAL_EXCEPTION) { 572 case XCPT_ACCESS_VIOLATION: 573 result = SIGSEGV; 574 break; 575 576 case XCPT_ILLEGAL_INSTRUCTION: 577 result = SIGILL; 578 break; 579 580 case XCPT_FLOAT_DIVIDE_BY_ZERO: 581 case XCPT_INTEGER_DIVIDE_BY_ZERO: 582 result = SIGFPE; 583 break; 584 585 default: 586 result = codes.codeResult; 587 break; 588 } 589 } 590 591 if (exitcode) { 592 *exitcode = result; 593 } 594 595 if (exitwhy) { 596 *exitwhy = why; 597 } 598} 599 600 601 602APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc, 603 int *exitcode, 604 apr_exit_why_e *exitwhy, 605 apr_wait_how_e waithow, 606 apr_pool_t *p) 607{ 608 RESULTCODES codes; 609 ULONG rc; 610 PID pid; 611 612 rc = DosWaitChild(DCWA_PROCESSTREE, waithow == APR_WAIT ? DCWW_WAIT : DCWW_NOWAIT, &codes, &pid, 0); 613 614 if (rc == 0) { 615 proc->pid = pid; 616 proces_result_codes(codes, exitcode, exitwhy); 617 return APR_CHILD_DONE; 618 } else if (rc == ERROR_CHILD_NOT_COMPLETE) { 619 return APR_CHILD_NOTDONE; 620 } 621 622 return APR_OS2_STATUS(rc); 623} 624 625 626 627APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, 628 int *exitcode, apr_exit_why_e *exitwhy, 629 apr_wait_how_e waithow) 630{ 631 RESULTCODES codes; 632 ULONG rc; 633 PID pid; 634 rc = DosWaitChild(DCWA_PROCESS, waithow == APR_WAIT ? DCWW_WAIT : DCWW_NOWAIT, &codes, &pid, proc->pid); 635 636 if (rc == 0) { 637 proces_result_codes(codes, exitcode, exitwhy); 638 return APR_CHILD_DONE; 639 } else if (rc == ERROR_CHILD_NOT_COMPLETE) { 640 return APR_CHILD_NOTDONE; 641 } 642 643 return APR_OS2_STATUS(rc); 644} 645 646 647 648APR_DECLARE(apr_status_t) apr_proc_detach(int daemonize) 649{ 650 return APR_ENOTIMPL; 651} 652 653APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr, 654 const char *username, 655 const char *password) 656{ 657 return APR_ENOTIMPL; 658} 659 660APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr, 661 const char *groupname) 662{ 663 return APR_ENOTIMPL; 664} 665