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