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_strings.h" 19#include "apr_portable.h" 20#include "apr_signal.h" 21#include "apr_random.h" 22 23#include <crt_externs.h> 24#include <spawn.h> 25 26/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE 27 * requested for a specific child handle; 28 */ 29static apr_file_t no_file = { NULL, -1, }; 30 31APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new, 32 apr_pool_t *pool) 33{ 34 (*new) = (apr_procattr_t *)apr_pcalloc(pool, sizeof(apr_procattr_t)); 35 36 if ((*new) == NULL) { 37 return APR_ENOMEM; 38 } 39 (*new)->pool = pool; 40 (*new)->cmdtype = APR_PROGRAM; 41 (*new)->uid = (*new)->gid = -1; 42 return APR_SUCCESS; 43} 44 45APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, 46 apr_int32_t in, 47 apr_int32_t out, 48 apr_int32_t err) 49{ 50 apr_status_t rv; 51 52 if ((in != APR_NO_PIPE) && (in != APR_NO_FILE)) { 53 /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while 54 * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose 55 * the CHILD/PARENT blocking flags for the stdin pipe. 56 * stdout/stderr map to the correct mode by default. 57 */ 58 if (in == APR_CHILD_BLOCK) 59 in = APR_READ_BLOCK; 60 else if (in == APR_PARENT_BLOCK) 61 in = APR_WRITE_BLOCK; 62 63 if ((rv = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in, 64 in, attr->pool)) == APR_SUCCESS) 65 rv = apr_file_inherit_unset(attr->parent_in); 66 if (rv != APR_SUCCESS) 67 return rv; 68 } 69 else if (in == APR_NO_FILE) 70 attr->child_in = &no_file; 71 72 if ((out != APR_NO_PIPE) && (out != APR_NO_FILE)) { 73 if ((rv = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out, 74 out, attr->pool)) == APR_SUCCESS) 75 rv = apr_file_inherit_unset(attr->parent_out); 76 if (rv != APR_SUCCESS) 77 return rv; 78 } 79 else if (out == APR_NO_FILE) 80 attr->child_out = &no_file; 81 82 if ((err != APR_NO_PIPE) && (err != APR_NO_FILE)) { 83 if ((rv = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err, 84 err, attr->pool)) == APR_SUCCESS) 85 rv = apr_file_inherit_unset(attr->parent_err); 86 if (rv != APR_SUCCESS) 87 return rv; 88 } 89 else if (err == APR_NO_FILE) 90 attr->child_err = &no_file; 91 92 return APR_SUCCESS; 93} 94 95 96APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, 97 apr_file_t *child_in, 98 apr_file_t *parent_in) 99{ 100 apr_status_t rv = APR_SUCCESS; 101 102 if (attr->child_in == NULL && attr->parent_in == NULL 103 && child_in == NULL && parent_in == NULL) 104 if ((rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in, 105 attr->pool)) == APR_SUCCESS) 106 rv = apr_file_inherit_unset(attr->parent_in); 107 108 if (child_in != NULL && rv == APR_SUCCESS) { 109 if (attr->child_in && (attr->child_in->filedes != -1)) 110 rv = apr_file_dup2(attr->child_in, child_in, attr->pool); 111 else { 112 attr->child_in = NULL; 113 if ((rv = apr_file_dup(&attr->child_in, child_in, attr->pool)) 114 == APR_SUCCESS) 115 rv = apr_file_inherit_set(attr->child_in); 116 } 117 } 118 119 if (parent_in != NULL && rv == APR_SUCCESS) { 120 if (attr->parent_in) 121 rv = apr_file_dup2(attr->parent_in, parent_in, attr->pool); 122 else 123 rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool); 124 } 125 126 return rv; 127} 128 129 130APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, 131 apr_file_t *child_out, 132 apr_file_t *parent_out) 133{ 134 apr_status_t rv = APR_SUCCESS; 135 136 if (attr->child_out == NULL && attr->parent_out == NULL 137 && child_out == NULL && parent_out == NULL) 138 if ((rv = apr_file_pipe_create(&attr->parent_out, &attr->child_out, 139 attr->pool)) == APR_SUCCESS) 140 rv = apr_file_inherit_unset(attr->parent_out); 141 142 if (child_out != NULL && rv == APR_SUCCESS) { 143 if (attr->child_out && (attr->child_out->filedes != -1)) 144 rv = apr_file_dup2(attr->child_out, child_out, attr->pool); 145 else { 146 attr->child_out = NULL; 147 if ((rv = apr_file_dup(&attr->child_out, child_out, attr->pool)) 148 == APR_SUCCESS) 149 rv = apr_file_inherit_set(attr->child_out); 150 } 151 } 152 153 if (parent_out != NULL && rv == APR_SUCCESS) { 154 if (attr->parent_out) 155 rv = apr_file_dup2(attr->parent_out, parent_out, attr->pool); 156 else 157 rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool); 158 } 159 160 return rv; 161} 162 163 164APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, 165 apr_file_t *child_err, 166 apr_file_t *parent_err) 167{ 168 apr_status_t rv = APR_SUCCESS; 169 170 if (attr->child_err == NULL && attr->parent_err == NULL 171 && child_err == NULL && parent_err == NULL) 172 if ((rv = apr_file_pipe_create(&attr->parent_err, &attr->child_err, 173 attr->pool)) == APR_SUCCESS) 174 rv = apr_file_inherit_unset(attr->parent_err); 175 176 if (child_err != NULL && rv == APR_SUCCESS) { 177 if (attr->child_err && (attr->child_err->filedes != -1)) 178 rv = apr_file_dup2(attr->child_err, child_err, attr->pool); 179 else { 180 attr->child_err = NULL; 181 if ((rv = apr_file_dup(&attr->child_err, child_err, attr->pool)) 182 == APR_SUCCESS) 183 rv = apr_file_inherit_set(attr->child_err); 184 } 185 } 186 if (parent_err != NULL && rv == APR_SUCCESS) { 187 if (attr->parent_err) 188 rv = apr_file_dup2(attr->parent_err, parent_err, attr->pool); 189 else 190 rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool); 191 } 192 193 return rv; 194} 195 196 197APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, 198 const char *dir) 199{ 200 attr->currdir = apr_pstrdup(attr->pool, dir); 201 if (attr->currdir) { 202 return APR_SUCCESS; 203 } 204 205 return APR_ENOMEM; 206} 207 208APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr, 209 apr_cmdtype_e cmd) 210{ 211 attr->cmdtype = cmd; 212 return APR_SUCCESS; 213} 214 215APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, 216 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 memset(proc, 0, sizeof(apr_proc_t)); 227 228 if ((pid = fork()) < 0) { 229 return errno; 230 } 231 else if (pid == 0) { 232 proc->pid = getpid(); 233 234 apr_random_after_fork(proc); 235 236 return APR_INCHILD; 237 } 238 239 proc->pid = pid; 240 241 return APR_INPARENT; 242} 243 244static apr_status_t limit_proc(apr_procattr_t *attr) 245{ 246#if APR_HAVE_STRUCT_RLIMIT && APR_HAVE_SETRLIMIT 247#ifdef RLIMIT_CPU 248 if (attr->limit_cpu != NULL) { 249 if ((setrlimit(RLIMIT_CPU, attr->limit_cpu)) != 0) { 250 return errno; 251 } 252 } 253#endif 254#ifdef RLIMIT_NPROC 255 if (attr->limit_nproc != NULL) { 256 if ((setrlimit(RLIMIT_NPROC, attr->limit_nproc)) != 0) { 257 return errno; 258 } 259 } 260#endif 261#ifdef RLIMIT_NOFILE 262 if (attr->limit_nofile != NULL) { 263 if ((setrlimit(RLIMIT_NOFILE, attr->limit_nofile)) != 0) { 264 return errno; 265 } 266 } 267#endif 268#if defined(RLIMIT_AS) 269 if (attr->limit_mem != NULL) { 270 if ((setrlimit(RLIMIT_AS, attr->limit_mem)) != 0) { 271 return errno; 272 } 273 } 274#elif defined(RLIMIT_DATA) 275 if (attr->limit_mem != NULL) { 276 if ((setrlimit(RLIMIT_DATA, attr->limit_mem)) != 0) { 277 return errno; 278 } 279 } 280#elif defined(RLIMIT_VMEM) 281 if (attr->limit_mem != NULL) { 282 if ((setrlimit(RLIMIT_VMEM, attr->limit_mem)) != 0) { 283 return errno; 284 } 285 } 286#endif 287#else 288 /* 289 * Maybe make a note in error_log that setrlimit isn't supported?? 290 */ 291 292#endif 293 return APR_SUCCESS; 294} 295 296APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr, 297 apr_child_errfn_t *errfn) 298{ 299 attr->errfn = errfn; 300 return APR_SUCCESS; 301} 302 303APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr, 304 apr_int32_t chk) 305{ 306 attr->errchk = chk; 307 return APR_SUCCESS; 308} 309 310APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr, 311 apr_int32_t addrspace) 312{ 313 /* won't ever be used on this platform, so don't save the flag */ 314 return APR_SUCCESS; 315} 316 317APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr, 318 const char *username, 319 const char *password) 320{ 321 apr_status_t rv; 322 apr_gid_t gid; 323 324 if ((rv = apr_uid_get(&attr->uid, &gid, username, 325 attr->pool)) != APR_SUCCESS) { 326 attr->uid = -1; 327 return rv; 328 } 329 330 /* Use default user group if not already set */ 331 if (attr->gid == -1) { 332 attr->gid = gid; 333 } 334 return APR_SUCCESS; 335} 336 337APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr, 338 const char *groupname) 339{ 340 apr_status_t rv; 341 342 if ((rv = apr_gid_get(&attr->gid, groupname, attr->pool)) != APR_SUCCESS) 343 attr->gid = -1; 344 return rv; 345} 346 347APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new, 348 const char *progname, 349 const char * const *args, 350 const char * const *env, 351 apr_procattr_t *attr, 352 apr_pool_t *pool) 353{ 354 int i; 355 const char * const empty_envp[] = {NULL}; 356 357 if (!env) { /* Specs require an empty array instead of NULL; 358 * Purify will trigger a failure, even if many 359 * implementations don't. 360 */ 361 env = empty_envp; 362 } 363 364 new->in = attr->parent_in; 365 new->err = attr->parent_err; 366 new->out = attr->parent_out; 367 368 if (attr->errchk) { 369 if (attr->currdir) { 370 if (access(attr->currdir, X_OK) == -1) { 371 /* chdir() in child wouldn't have worked */ 372 return errno; 373 } 374 } 375 376 if (attr->cmdtype == APR_PROGRAM || 377 attr->cmdtype == APR_PROGRAM_ENV || 378 *progname == '/') { 379 /* for both of these values of cmdtype, caller must pass 380 * full path, so it is easy to check; 381 * caller can choose to pass full path for other 382 * values of cmdtype 383 */ 384 if (access(progname, X_OK) == -1) { 385 /* exec*() in child wouldn't have worked */ 386 return errno; 387 } 388 } 389 else { 390 /* todo: search PATH for progname then try to access it */ 391 } 392 } 393 394 if ((new->pid = fork()) < 0) { 395 return errno; 396 } 397 else if (new->pid == 0) { 398 /* child process */ 399 400 /* 401 * If we do exec cleanup before the dup2() calls to set up pipes 402 * on 0-2, we accidentally close the pipes used by programs like 403 * mod_cgid. 404 * 405 * If we do exec cleanup after the dup2() calls, cleanup can accidentally 406 * close our pipes which replaced any files which previously had 407 * descriptors 0-2. 408 * 409 * The solution is to kill the cleanup for the pipes, then do 410 * exec cleanup, then do the dup2() calls. 411 */ 412 413 if (attr->child_in) { 414 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_in), 415 attr->child_in, apr_unix_file_cleanup); 416 } 417 418 if (attr->child_out) { 419 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_out), 420 attr->child_out, apr_unix_file_cleanup); 421 } 422 423 if (attr->child_err) { 424 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_err), 425 attr->child_err, apr_unix_file_cleanup); 426 } 427 428 apr_pool_cleanup_for_exec(); 429 430 if ((attr->child_in) && (attr->child_in->filedes == -1)) { 431 close(STDIN_FILENO); 432 } 433 else if (attr->child_in && 434 attr->child_in->filedes != STDIN_FILENO) { 435 dup2(attr->child_in->filedes, STDIN_FILENO); 436 apr_file_close(attr->child_in); 437 } 438 439 if ((attr->child_out) && (attr->child_out->filedes == -1)) { 440 close(STDOUT_FILENO); 441 } 442 else if (attr->child_out && 443 attr->child_out->filedes != STDOUT_FILENO) { 444 dup2(attr->child_out->filedes, STDOUT_FILENO); 445 apr_file_close(attr->child_out); 446 } 447 448 if ((attr->child_err) && (attr->child_err->filedes == -1)) { 449 close(STDERR_FILENO); 450 } 451 else if (attr->child_err && 452 attr->child_err->filedes != STDERR_FILENO) { 453 dup2(attr->child_err->filedes, STDERR_FILENO); 454 apr_file_close(attr->child_err); 455 } 456 457 apr_signal(SIGCHLD, SIG_DFL); /* not sure if this is needed or not */ 458 459 if (attr->currdir != NULL) { 460 if (chdir(attr->currdir) == -1) { 461 if (attr->errfn) { 462 attr->errfn(pool, errno, "change of working directory failed"); 463 } 464 _exit(-1); /* We have big problems, the child should exit. */ 465 } 466 } 467 468 /* Only try to switch if we are running as root */ 469 if (attr->gid != -1 && !geteuid()) { 470 if (setgid(attr->gid)) { 471 if (attr->errfn) { 472 attr->errfn(pool, errno, "setting of group failed"); 473 } 474 _exit(-1); /* We have big problems, the child should exit. */ 475 } 476 } 477 478 if (attr->uid != -1 && !geteuid()) { 479 if (setuid(attr->uid)) { 480 if (attr->errfn) { 481 attr->errfn(pool, errno, "setting of user failed"); 482 } 483 _exit(-1); /* We have big problems, the child should exit. */ 484 } 485 } 486 487 if (limit_proc(attr) != APR_SUCCESS) { 488 if (attr->errfn) { 489 attr->errfn(pool, errno, "setting of resource limits failed"); 490 } 491 _exit(-1); /* We have big problems, the child should exit. */ 492 } 493 494 if (attr->cmdtype == APR_SHELLCMD || 495 attr->cmdtype == APR_SHELLCMD_ENV) { 496 int onearg_len = 0; 497 const char *newargs[4]; 498 499 newargs[0] = SHELL_PATH; 500 newargs[1] = "-c"; 501 502 i = 0; 503 while (args[i]) { 504 onearg_len += strlen(args[i]); 505 onearg_len++; /* for space delimiter */ 506 i++; 507 } 508 509 switch(i) { 510 case 0: 511 /* bad parameters; we're doomed */ 512 break; 513 case 1: 514 /* no args, or caller already built a single string from 515 * progname and args 516 */ 517 newargs[2] = args[0]; 518 break; 519 default: 520 { 521 char *ch, *onearg; 522 523 ch = onearg = apr_palloc(pool, onearg_len); 524 i = 0; 525 while (args[i]) { 526 size_t len = strlen(args[i]); 527 528 memcpy(ch, args[i], len); 529 ch += len; 530 *ch = ' '; 531 ++ch; 532 ++i; 533 } 534 --ch; /* back up to trailing blank */ 535 *ch = '\0'; 536 newargs[2] = onearg; 537 } 538 } 539 540 newargs[3] = NULL; 541 542 if (attr->detached) { 543 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 544 } 545 546 if (attr->cmdtype == APR_SHELLCMD) { 547 execve(SHELL_PATH, (char * const *) newargs, (char * const *)env); 548 } 549 else { 550 execv(SHELL_PATH, (char * const *)newargs); 551 } 552 } 553 else if (attr->cmdtype == APR_PROGRAM) { 554 if (attr->detached) { 555 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 556 } 557 558 execve(progname, (char * const *)args, (char * const *)env); 559 } 560 else if (attr->cmdtype == APR_PROGRAM_ENV) { 561 if (attr->detached) { 562 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 563 } 564 565 execv(progname, (char * const *)args); 566 } 567 else { 568 /* APR_PROGRAM_PATH */ 569 if (attr->detached) { 570 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 571 } 572 573 execvp(progname, (char * const *)args); 574 } 575 if (attr->errfn) { 576 char *desc; 577 578 desc = apr_psprintf(pool, "exec of '%s' failed", 579 progname); 580 attr->errfn(pool, errno, desc); 581 } 582 583 _exit(-1); /* if we get here, there is a problem, so exit with an 584 * error code. */ 585 } 586 587 /* Parent process */ 588 if (attr->child_in && (attr->child_in->filedes != -1)) { 589 apr_file_close(attr->child_in); 590 } 591 592 if (attr->child_out && (attr->child_out->filedes != -1)) { 593 apr_file_close(attr->child_out); 594 } 595 596 if (attr->child_err && (attr->child_err->filedes != -1)) { 597 apr_file_close(attr->child_err); 598 } 599 600 return APR_SUCCESS; 601} 602 603APR_DECLARE(apr_status_t) apr_proc_spawn(apr_proc_t *new, 604 const char *progname, 605 const char * const *args, 606 const char * const *env, 607 apr_procattr_t *attr, 608 apr_pool_t *pool) 609{ 610 posix_spawn_file_actions_t file_actions; 611 int status = APR_EGENERAL; 612 613 new->in = attr->parent_in; 614 new->err = attr->parent_err; 615 new->out = attr->parent_out; 616 617 // XXX: attr->errchk 618 619 // XXX: Are those apr_pool_cleanup_* calls needed? 620 621 posix_spawn_file_actions_init(&file_actions); 622 623 if ((attr->child_in) && (attr->child_in->filedes == -1)) { 624 posix_spawn_file_actions_addclose(&file_actions, STDIN_FILENO); 625 } 626 else if (attr->child_in) { 627 posix_spawn_file_actions_adddup2(&file_actions, attr->child_in->filedes, STDIN_FILENO); 628 posix_spawn_file_actions_addclose(&file_actions, attr->child_in->filedes); 629 } 630 631 if ((attr->child_out) && (attr->child_out->filedes == -1)) { 632 posix_spawn_file_actions_addclose(&file_actions, STDOUT_FILENO); 633 } 634 else if (attr->child_out) { 635 posix_spawn_file_actions_adddup2(&file_actions, attr->child_out->filedes, STDOUT_FILENO); 636 posix_spawn_file_actions_addclose(&file_actions, attr->child_out->filedes); 637 } 638 639 if ((attr->child_err) && (attr->child_err->filedes == -1)) { 640 posix_spawn_file_actions_addclose(&file_actions, STDERR_FILENO); 641 } 642 else if (attr->child_err) { 643 posix_spawn_file_actions_adddup2(&file_actions, attr->child_err->filedes, STDERR_FILENO); 644 posix_spawn_file_actions_addclose(&file_actions, attr->child_err->filedes); 645 } 646 647 // XXX: signal 648 // XXX: currdir, gid, uid, limit_proc 649 650 /* Only support APR_PROGRAM_PATH for now. */ 651 if (attr->cmdtype == APR_PROGRAM_PATH) { 652 /* Pass existing environment; as documented, APR_PROGRAM_PATH ignores the env argument. */ 653 status = posix_spawnp(&new->pid, progname, &file_actions, NULL, args, (const char **)*_NSGetEnviron()); 654 if (status != 0) { 655 if (attr->errfn) { 656 char *desc; 657 658 desc = apr_psprintf(pool, "posix_spawn of '%s' failed", progname); 659 attr->errfn(pool, status, desc); 660 } 661 } 662 } 663 664 posix_spawn_file_actions_destroy(&file_actions); 665 666 if (attr->child_in && (attr->child_in->filedes != -1)) { 667 apr_file_close(attr->child_in); 668 } 669 670 if (attr->child_out && (attr->child_out->filedes != -1)) { 671 apr_file_close(attr->child_out); 672 } 673 674 if (attr->child_err && (attr->child_err->filedes != -1)) { 675 apr_file_close(attr->child_err); 676 } 677 678 return status; 679} 680 681APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc, 682 int *exitcode, 683 apr_exit_why_e *exitwhy, 684 apr_wait_how_e waithow, 685 apr_pool_t *p) 686{ 687 proc->pid = -1; 688 return apr_proc_wait(proc, exitcode, exitwhy, waithow); 689} 690 691APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, 692 int *exitcode, apr_exit_why_e *exitwhy, 693 apr_wait_how_e waithow) 694{ 695 pid_t pstatus; 696 int waitpid_options = WUNTRACED; 697 int exit_int; 698 int ignore; 699 apr_exit_why_e ignorewhy; 700 701 if (exitcode == NULL) { 702 exitcode = &ignore; 703 } 704 705 if (exitwhy == NULL) { 706 exitwhy = &ignorewhy; 707 } 708 709 if (waithow != APR_WAIT) { 710 waitpid_options |= WNOHANG; 711 } 712 713 do { 714 pstatus = waitpid(proc->pid, &exit_int, waitpid_options); 715 } while (pstatus < 0 && errno == EINTR); 716 717 if (pstatus > 0) { 718 proc->pid = pstatus; 719 720 if (WIFEXITED(exit_int)) { 721 *exitwhy = APR_PROC_EXIT; 722 *exitcode = WEXITSTATUS(exit_int); 723 } 724 else if (WIFSIGNALED(exit_int)) { 725 *exitwhy = APR_PROC_SIGNAL; 726 727#ifdef WCOREDUMP 728 if (WCOREDUMP(exit_int)) { 729 *exitwhy |= APR_PROC_SIGNAL_CORE; 730 } 731#endif 732 733 *exitcode = WTERMSIG(exit_int); 734 } 735 else { 736 /* unexpected condition */ 737 return APR_EGENERAL; 738 } 739 740 return APR_CHILD_DONE; 741 } 742 else if (pstatus == 0) { 743 return APR_CHILD_NOTDONE; 744 } 745 746 return errno; 747} 748 749#if APR_HAVE_STRUCT_RLIMIT 750APR_DECLARE(apr_status_t) apr_procattr_limit_set(apr_procattr_t *attr, 751 apr_int32_t what, 752 struct rlimit *limit) 753{ 754 switch(what) { 755 case APR_LIMIT_CPU: 756#ifdef RLIMIT_CPU 757 attr->limit_cpu = limit; 758 break; 759#else 760 return APR_ENOTIMPL; 761#endif 762 763 case APR_LIMIT_MEM: 764#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) 765 attr->limit_mem = limit; 766 break; 767#else 768 return APR_ENOTIMPL; 769#endif 770 771 case APR_LIMIT_NPROC: 772#ifdef RLIMIT_NPROC 773 attr->limit_nproc = limit; 774 break; 775#else 776 return APR_ENOTIMPL; 777#endif 778 779 case APR_LIMIT_NOFILE: 780#ifdef RLIMIT_NOFILE 781 attr->limit_nofile = limit; 782 break; 783#else 784 return APR_ENOTIMPL; 785#endif 786 787 } 788 789 return APR_SUCCESS; 790} 791#endif /* APR_HAVE_STRUCT_RLIMIT */ 792 793