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