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 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 235 apr_random_after_fork(proc); 236 237 return APR_INCHILD; 238 } 239 240 proc->pid = pid; 241 proc->in = NULL; 242 proc->out = NULL; 243 proc->err = NULL; 244 245 return APR_INPARENT; 246} 247 248static apr_status_t limit_proc(apr_procattr_t *attr) 249{ 250#if APR_HAVE_STRUCT_RLIMIT && APR_HAVE_SETRLIMIT 251#ifdef RLIMIT_CPU 252 if (attr->limit_cpu != NULL) { 253 if ((setrlimit(RLIMIT_CPU, attr->limit_cpu)) != 0) { 254 return errno; 255 } 256 } 257#endif 258#ifdef RLIMIT_NPROC 259 if (attr->limit_nproc != NULL) { 260 if ((setrlimit(RLIMIT_NPROC, attr->limit_nproc)) != 0) { 261 return errno; 262 } 263 } 264#endif 265#ifdef RLIMIT_NOFILE 266 if (attr->limit_nofile != NULL) { 267 if ((setrlimit(RLIMIT_NOFILE, attr->limit_nofile)) != 0) { 268 return errno; 269 } 270 } 271#endif 272#if defined(RLIMIT_AS) 273 if (attr->limit_mem != NULL) { 274 if ((setrlimit(RLIMIT_AS, attr->limit_mem)) != 0) { 275 return errno; 276 } 277 } 278#elif defined(RLIMIT_DATA) 279 if (attr->limit_mem != NULL) { 280 if ((setrlimit(RLIMIT_DATA, attr->limit_mem)) != 0) { 281 return errno; 282 } 283 } 284#elif defined(RLIMIT_VMEM) 285 if (attr->limit_mem != NULL) { 286 if ((setrlimit(RLIMIT_VMEM, attr->limit_mem)) != 0) { 287 return errno; 288 } 289 } 290#endif 291#else 292 /* 293 * Maybe make a note in error_log that setrlimit isn't supported?? 294 */ 295 296#endif 297 return APR_SUCCESS; 298} 299 300APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr, 301 apr_child_errfn_t *errfn) 302{ 303 attr->errfn = errfn; 304 return APR_SUCCESS; 305} 306 307APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr, 308 apr_int32_t chk) 309{ 310 attr->errchk = chk; 311 return APR_SUCCESS; 312} 313 314APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr, 315 apr_int32_t addrspace) 316{ 317 /* won't ever be used on this platform, so don't save the flag */ 318 return APR_SUCCESS; 319} 320 321APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr, 322 const char *username, 323 const char *password) 324{ 325 apr_status_t rv; 326 apr_gid_t gid; 327 328 if ((rv = apr_uid_get(&attr->uid, &gid, username, 329 attr->pool)) != APR_SUCCESS) { 330 attr->uid = -1; 331 return rv; 332 } 333 334 /* Use default user group if not already set */ 335 if (attr->gid == -1) { 336 attr->gid = gid; 337 } 338 return APR_SUCCESS; 339} 340 341APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr, 342 const char *groupname) 343{ 344 apr_status_t rv; 345 346 if ((rv = apr_gid_get(&attr->gid, groupname, attr->pool)) != APR_SUCCESS) 347 attr->gid = -1; 348 return rv; 349} 350 351APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new, 352 const char *progname, 353 const char * const *args, 354 const char * const *env, 355 apr_procattr_t *attr, 356 apr_pool_t *pool) 357{ 358 int i; 359 const char * const empty_envp[] = {NULL}; 360 361 if (!env) { /* Specs require an empty array instead of NULL; 362 * Purify will trigger a failure, even if many 363 * implementations don't. 364 */ 365 env = empty_envp; 366 } 367 368 new->in = attr->parent_in; 369 new->err = attr->parent_err; 370 new->out = attr->parent_out; 371 372 if (attr->errchk) { 373 if (attr->currdir) { 374 if (access(attr->currdir, X_OK) == -1) { 375 /* chdir() in child wouldn't have worked */ 376 return errno; 377 } 378 } 379 380 if (attr->cmdtype == APR_PROGRAM || 381 attr->cmdtype == APR_PROGRAM_ENV || 382 *progname == '/') { 383 /* for both of these values of cmdtype, caller must pass 384 * full path, so it is easy to check; 385 * caller can choose to pass full path for other 386 * values of cmdtype 387 */ 388 if (access(progname, X_OK) == -1) { 389 /* exec*() in child wouldn't have worked */ 390 return errno; 391 } 392 } 393 else { 394 /* todo: search PATH for progname then try to access it */ 395 } 396 } 397 398 if ((new->pid = fork()) < 0) { 399 return errno; 400 } 401 else if (new->pid == 0) { 402 int status; 403 /* child process */ 404 405 /* 406 * If we do exec cleanup before the dup2() calls to set up pipes 407 * on 0-2, we accidentally close the pipes used by programs like 408 * mod_cgid. 409 * 410 * If we do exec cleanup after the dup2() calls, cleanup can accidentally 411 * close our pipes which replaced any files which previously had 412 * descriptors 0-2. 413 * 414 * The solution is to kill the cleanup for the pipes, then do 415 * exec cleanup, then do the dup2() calls. 416 */ 417 418 if (attr->child_in) { 419 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_in), 420 attr->child_in, apr_unix_file_cleanup); 421 } 422 423 if (attr->child_out) { 424 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_out), 425 attr->child_out, apr_unix_file_cleanup); 426 } 427 428 if (attr->child_err) { 429 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_err), 430 attr->child_err, apr_unix_file_cleanup); 431 } 432 433 apr_pool_cleanup_for_exec(); 434 435 if ((attr->child_in) && (attr->child_in->filedes == -1)) { 436 close(STDIN_FILENO); 437 } 438 else if (attr->child_in) { 439 dup2(attr->child_in->filedes, STDIN_FILENO); 440 apr_file_close(attr->child_in); 441 } 442 443 if ((attr->child_out) && (attr->child_out->filedes == -1)) { 444 close(STDOUT_FILENO); 445 } 446 else if (attr->child_out) { 447 dup2(attr->child_out->filedes, STDOUT_FILENO); 448 apr_file_close(attr->child_out); 449 } 450 451 if ((attr->child_err) && (attr->child_err->filedes == -1)) { 452 close(STDERR_FILENO); 453 } 454 else if (attr->child_err) { 455 dup2(attr->child_err->filedes, STDERR_FILENO); 456 apr_file_close(attr->child_err); 457 } 458 459 apr_signal(SIGCHLD, SIG_DFL); /* not sure if this is needed or not */ 460 461 if (attr->currdir != NULL) { 462 if (chdir(attr->currdir) == -1) { 463 if (attr->errfn) { 464 attr->errfn(pool, errno, "change of working directory failed"); 465 } 466 _exit(-1); /* We have big problems, the child should exit. */ 467 } 468 } 469 470 /* Only try to switch if we are running as root */ 471 if (attr->gid != -1 && !geteuid()) { 472 if ((status = setgid(attr->gid))) { 473 if (attr->errfn) { 474 attr->errfn(pool, errno, "setting of group failed"); 475 } 476 _exit(-1); /* We have big problems, the child should exit. */ 477 } 478 } 479 480 if (attr->uid != -1 && !geteuid()) { 481 if ((status = setuid(attr->uid))) { 482 if (attr->errfn) { 483 attr->errfn(pool, errno, "setting of user failed"); 484 } 485 _exit(-1); /* We have big problems, the child should exit. */ 486 } 487 } 488 489 if ((status = limit_proc(attr)) != APR_SUCCESS) { 490 if (attr->errfn) { 491 attr->errfn(pool, errno, "setting of resource limits failed"); 492 } 493 _exit(-1); /* We have big problems, the child should exit. */ 494 } 495 496 if (attr->cmdtype == APR_SHELLCMD || 497 attr->cmdtype == APR_SHELLCMD_ENV) { 498 int onearg_len = 0; 499 const char *newargs[4]; 500 501 newargs[0] = SHELL_PATH; 502 newargs[1] = "-c"; 503 504 i = 0; 505 while (args[i]) { 506 onearg_len += strlen(args[i]); 507 onearg_len++; /* for space delimiter */ 508 i++; 509 } 510 511 switch(i) { 512 case 0: 513 /* bad parameters; we're doomed */ 514 break; 515 case 1: 516 /* no args, or caller already built a single string from 517 * progname and args 518 */ 519 newargs[2] = args[0]; 520 break; 521 default: 522 { 523 char *ch, *onearg; 524 525 ch = onearg = apr_palloc(pool, onearg_len); 526 i = 0; 527 while (args[i]) { 528 size_t len = strlen(args[i]); 529 530 memcpy(ch, args[i], len); 531 ch += len; 532 *ch = ' '; 533 ++ch; 534 ++i; 535 } 536 --ch; /* back up to trailing blank */ 537 *ch = '\0'; 538 newargs[2] = onearg; 539 } 540 } 541 542 newargs[3] = NULL; 543 544 if (attr->detached) { 545 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 546 } 547 548 if (attr->cmdtype == APR_SHELLCMD) { 549 execve(SHELL_PATH, (char * const *) newargs, (char * const *)env); 550 } 551 else { 552 execv(SHELL_PATH, (char * const *)newargs); 553 } 554 } 555 else if (attr->cmdtype == APR_PROGRAM) { 556 if (attr->detached) { 557 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 558 } 559 560 execve(progname, (char * const *)args, (char * const *)env); 561 } 562 else if (attr->cmdtype == APR_PROGRAM_ENV) { 563 if (attr->detached) { 564 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 565 } 566 567 execv(progname, (char * const *)args); 568 } 569 else { 570 /* APR_PROGRAM_PATH */ 571 if (attr->detached) { 572 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); 573 } 574 575 execvp(progname, (char * const *)args); 576 } 577 if (attr->errfn) { 578 char *desc; 579 580 desc = apr_psprintf(pool, "exec of '%s' failed", 581 progname); 582 attr->errfn(pool, errno, desc); 583 } 584 585 _exit(-1); /* if we get here, there is a problem, so exit with an 586 * error code. */ 587 } 588 589 /* Parent process */ 590 if (attr->child_in && (attr->child_in->filedes != -1)) { 591 apr_file_close(attr->child_in); 592 } 593 594 if (attr->child_out && (attr->child_out->filedes != -1)) { 595 apr_file_close(attr->child_out); 596 } 597 598 if (attr->child_err && (attr->child_err->filedes != -1)) { 599 apr_file_close(attr->child_err); 600 } 601 602 return APR_SUCCESS; 603} 604 605APR_DECLARE(apr_status_t) apr_proc_spawn(apr_proc_t *new, 606 const char *progname, 607 const char * const *args, 608 const char * const *env, 609 apr_procattr_t *attr, 610 apr_pool_t *pool) 611{ 612 posix_spawn_file_actions_t file_actions; 613 int status = APR_EGENERAL; 614 615 new->in = attr->parent_in; 616 new->err = attr->parent_err; 617 new->out = attr->parent_out; 618 619 // XXX: attr->errchk 620 621 // XXX: Are those apr_pool_cleanup_* calls needed? 622 623 posix_spawn_file_actions_init(&file_actions); 624 625 if ((attr->child_in) && (attr->child_in->filedes == -1)) { 626 posix_spawn_file_actions_addclose(&file_actions, STDIN_FILENO); 627 } 628 else if (attr->child_in) { 629 posix_spawn_file_actions_adddup2(&file_actions, attr->child_in->filedes, STDIN_FILENO); 630 posix_spawn_file_actions_addclose(&file_actions, attr->child_in->filedes); 631 } 632 633 if ((attr->child_out) && (attr->child_out->filedes == -1)) { 634 posix_spawn_file_actions_addclose(&file_actions, STDOUT_FILENO); 635 } 636 else if (attr->child_out) { 637 posix_spawn_file_actions_adddup2(&file_actions, attr->child_out->filedes, STDOUT_FILENO); 638 posix_spawn_file_actions_addclose(&file_actions, attr->child_out->filedes); 639 } 640 641 if ((attr->child_err) && (attr->child_err->filedes == -1)) { 642 posix_spawn_file_actions_addclose(&file_actions, STDERR_FILENO); 643 } 644 else if (attr->child_err) { 645 posix_spawn_file_actions_adddup2(&file_actions, attr->child_err->filedes, STDERR_FILENO); 646 posix_spawn_file_actions_addclose(&file_actions, attr->child_err->filedes); 647 } 648 649 // XXX: signal 650 // XXX: currdir, gid, uid, limit_proc 651 652 /* Only support APR_PROGRAM_PATH for now. */ 653 if (attr->cmdtype == APR_PROGRAM_PATH) { 654 /* Pass existing environment; as documented, APR_PROGRAM_PATH ignores the env argument. */ 655 status = posix_spawnp(&new->pid, progname, &file_actions, NULL, args, (const char **)*_NSGetEnviron()); 656 if (status != 0) { 657 if (attr->errfn) { 658 char *desc; 659 660 desc = apr_psprintf(pool, "posix_spawn of '%s' failed", progname); 661 attr->errfn(pool, status, desc); 662 } 663 } 664 } 665 666 posix_spawn_file_actions_destroy(&file_actions); 667 668 if (attr->child_in && (attr->child_in->filedes != -1)) { 669 apr_file_close(attr->child_in); 670 } 671 672 if (attr->child_out && (attr->child_out->filedes != -1)) { 673 apr_file_close(attr->child_out); 674 } 675 676 if (attr->child_err && (attr->child_err->filedes != -1)) { 677 apr_file_close(attr->child_err); 678 } 679 680 return status; 681} 682 683APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc, 684 int *exitcode, 685 apr_exit_why_e *exitwhy, 686 apr_wait_how_e waithow, 687 apr_pool_t *p) 688{ 689 proc->pid = -1; 690 return apr_proc_wait(proc, exitcode, exitwhy, waithow); 691} 692 693APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc, 694 int *exitcode, apr_exit_why_e *exitwhy, 695 apr_wait_how_e waithow) 696{ 697 pid_t pstatus; 698 int waitpid_options = WUNTRACED; 699 int exit_int; 700 int ignore; 701 apr_exit_why_e ignorewhy; 702 703 if (exitcode == NULL) { 704 exitcode = &ignore; 705 } 706 707 if (exitwhy == NULL) { 708 exitwhy = &ignorewhy; 709 } 710 711 if (waithow != APR_WAIT) { 712 waitpid_options |= WNOHANG; 713 } 714 715 do { 716 pstatus = waitpid(proc->pid, &exit_int, waitpid_options); 717 } while (pstatus < 0 && errno == EINTR); 718 719 if (pstatus > 0) { 720 proc->pid = pstatus; 721 722 if (WIFEXITED(exit_int)) { 723 *exitwhy = APR_PROC_EXIT; 724 *exitcode = WEXITSTATUS(exit_int); 725 } 726 else if (WIFSIGNALED(exit_int)) { 727 *exitwhy = APR_PROC_SIGNAL; 728 729#ifdef WCOREDUMP 730 if (WCOREDUMP(exit_int)) { 731 *exitwhy |= APR_PROC_SIGNAL_CORE; 732 } 733#endif 734 735 *exitcode = WTERMSIG(exit_int); 736 } 737 else { 738 /* unexpected condition */ 739 return APR_EGENERAL; 740 } 741 742 return APR_CHILD_DONE; 743 } 744 else if (pstatus == 0) { 745 return APR_CHILD_NOTDONE; 746 } 747 748 return errno; 749} 750 751#if APR_HAVE_STRUCT_RLIMIT 752APR_DECLARE(apr_status_t) apr_procattr_limit_set(apr_procattr_t *attr, 753 apr_int32_t what, 754 struct rlimit *limit) 755{ 756 switch(what) { 757 case APR_LIMIT_CPU: 758#ifdef RLIMIT_CPU 759 attr->limit_cpu = limit; 760 break; 761#else 762 return APR_ENOTIMPL; 763#endif 764 765 case APR_LIMIT_MEM: 766#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) 767 attr->limit_mem = limit; 768 break; 769#else 770 return APR_ENOTIMPL; 771#endif 772 773 case APR_LIMIT_NPROC: 774#ifdef RLIMIT_NPROC 775 attr->limit_nproc = limit; 776 break; 777#else 778 return APR_ENOTIMPL; 779#endif 780 781 case APR_LIMIT_NOFILE: 782#ifdef RLIMIT_NOFILE 783 attr->limit_nofile = limit; 784 break; 785#else 786 return APR_ENOTIMPL; 787#endif 788 789 } 790 791 return APR_SUCCESS; 792} 793#endif /* APR_HAVE_STRUCT_RLIMIT */ 794 795