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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "apr_arch_threadproc.h"
18 #include "apr_strings.h"
19 #include "apr_portable.h"
20 #include "apr_signal.h"
22 APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new,
25 (*new) = (apr_procattr_t *)apr_pcalloc(pool, sizeof(apr_procattr_t));
31 (*new)->cmdtype = APR_PROGRAM;
35 APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr,
42 if ((status = apr_file_pipe_create(&attr->child_in, &attr->parent_in,
43 attr->pool)) != APR_SUCCESS) {
50 case APR_PARENT_BLOCK:
51 apr_file_pipe_timeout_set(attr->child_in, 0);
54 apr_file_pipe_timeout_set(attr->parent_in, 0);
57 apr_file_pipe_timeout_set(attr->child_in, 0);
58 apr_file_pipe_timeout_set(attr->parent_in, 0);
63 if ((status = apr_file_pipe_create(&attr->parent_out, &attr->child_out,
64 attr->pool)) != APR_SUCCESS) {
71 case APR_PARENT_BLOCK:
72 apr_file_pipe_timeout_set(attr->child_out, 0);
75 apr_file_pipe_timeout_set(attr->parent_out, 0);
78 apr_file_pipe_timeout_set(attr->child_out, 0);
79 apr_file_pipe_timeout_set(attr->parent_out, 0);
84 if ((status = apr_file_pipe_create(&attr->parent_err, &attr->child_err,
85 attr->pool)) != APR_SUCCESS) {
92 case APR_PARENT_BLOCK:
93 apr_file_pipe_timeout_set(attr->child_err, 0);
96 apr_file_pipe_timeout_set(attr->parent_err, 0);
99 apr_file_pipe_timeout_set(attr->child_err, 0);
100 apr_file_pipe_timeout_set(attr->parent_err, 0);
108 APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr,
109 apr_file_t *child_in,
110 apr_file_t *parent_in)
112 apr_status_t rv = APR_SUCCESS;
114 if (attr->child_in == NULL && attr->parent_in == NULL)
115 rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in, attr->pool);
117 if (child_in != NULL && rv == APR_SUCCESS)
118 rv = apr_file_dup2(attr->child_in, child_in, attr->pool);
120 if (parent_in != NULL && rv == APR_SUCCESS)
121 rv = apr_file_dup2(attr->parent_in, parent_in, attr->pool);
127 APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr,
128 apr_file_t *child_out,
129 apr_file_t *parent_out)
131 apr_status_t rv = APR_SUCCESS;
133 if (attr->child_out == NULL && attr->parent_out == NULL)
134 rv = apr_file_pipe_create(&attr->child_out, &attr->parent_out, attr->pool);
136 if (child_out != NULL && rv == APR_SUCCESS)
137 rv = apr_file_dup2(attr->child_out, child_out, attr->pool);
139 if (parent_out != NULL && rv == APR_SUCCESS)
140 rv = apr_file_dup2(attr->parent_out, parent_out, attr->pool);
146 APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr,
147 apr_file_t *child_err,
148 apr_file_t *parent_err)
150 apr_status_t rv = APR_SUCCESS;
152 if (attr->child_err == NULL && attr->parent_err == NULL)
153 rv = apr_file_pipe_create(&attr->child_err, &attr->parent_err, attr->pool);
155 if (child_err != NULL && rv == APR_SUCCESS)
156 rv = apr_file_dup2(attr->child_err, child_err, attr->pool);
158 if (parent_err != NULL && rv == APR_SUCCESS)
159 rv = apr_file_dup2(attr->parent_err, parent_err, attr->pool);
165 APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr,
168 attr->currdir = apr_pstrdup(attr->pool, dir);
176 APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr,
183 APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr,
186 attr->detached = detach;
190 APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool)
194 if ((pid = fork()) < 0) {
214 static apr_status_t limit_proc(apr_procattr_t *attr)
216 #if APR_HAVE_STRUCT_RLIMIT && APR_HAVE_SETRLIMIT
218 if (attr->limit_cpu != NULL) {
219 if ((setrlimit(RLIMIT_CPU, attr->limit_cpu)) != 0) {
225 if (attr->limit_nproc != NULL) {
226 if ((setrlimit(RLIMIT_NPROC, attr->limit_nproc)) != 0) {
232 if (attr->limit_nofile != NULL) {
233 if ((setrlimit(RLIMIT_NOFILE, attr->limit_nofile)) != 0) {
238 #if defined(RLIMIT_AS)
239 if (attr->limit_mem != NULL) {
240 if ((setrlimit(RLIMIT_AS, attr->limit_mem)) != 0) {
244 #elif defined(RLIMIT_DATA)
245 if (attr->limit_mem != NULL) {
246 if ((setrlimit(RLIMIT_DATA, attr->limit_mem)) != 0) {
250 #elif defined(RLIMIT_VMEM)
251 if (attr->limit_mem != NULL) {
252 if ((setrlimit(RLIMIT_VMEM, attr->limit_mem)) != 0) {
259 * Maybe make a note in error_log that setrlimit isn't supported??
266 APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr,
267 apr_child_errfn_t *errfn)
273 APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr,
280 APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr,
281 apr_int32_t addrspace)
283 /* won't ever be used on this platform, so don't save the flag */
287 APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new,
288 const char *progname,
289 const char * const *args,
290 const char * const *env,
291 apr_procattr_t *attr,
295 const char * const empty_envp[] = {NULL};
297 if (!env) { /* Specs require an empty array instead of NULL;
298 * Purify will trigger a failure, even if many
299 * implementations don't.
304 new->in = attr->parent_in;
305 new->err = attr->parent_err;
306 new->out = attr->parent_out;
310 if (access(attr->currdir, X_OK) == -1) {
311 /* chdir() in child wouldn't have worked */
316 if (attr->cmdtype == APR_PROGRAM ||
317 attr->cmdtype == APR_PROGRAM_ENV ||
319 /* for both of these values of cmdtype, caller must pass
320 * full path, so it is easy to check;
321 * caller can choose to pass full path for other
324 if (access(progname, R_OK|X_OK) == -1) {
325 /* exec*() in child wouldn't have worked */
330 /* todo: search PATH for progname then try to access it */
334 if ((new->pid = fork()) < 0) {
337 else if (new->pid == 0) {
342 * If we do exec cleanup before the dup2() calls to set up pipes
343 * on 0-2, we accidentally close the pipes used by programs like
346 * If we do exec cleanup after the dup2() calls, cleanup can accidentally
347 * close our pipes which replaced any files which previously had
350 * The solution is to kill the cleanup for the pipes, then do
351 * exec cleanup, then do the dup2() calls.
354 if (attr->child_in) {
355 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_in),
356 attr->child_in, apr_unix_file_cleanup);
359 if (attr->child_out) {
360 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_out),
361 attr->child_out, apr_unix_file_cleanup);
364 if (attr->child_err) {
365 apr_pool_cleanup_kill(apr_file_pool_get(attr->child_err),
366 attr->child_err, apr_unix_file_cleanup);
369 apr_pool_cleanup_for_exec();
371 if (attr->child_in) {
372 apr_file_close(attr->parent_in);
373 dup2(attr->child_in->filedes, STDIN_FILENO);
374 apr_file_close(attr->child_in);
377 if (attr->child_out) {
378 apr_file_close(attr->parent_out);
379 dup2(attr->child_out->filedes, STDOUT_FILENO);
380 apr_file_close(attr->child_out);
383 if (attr->child_err) {
384 apr_file_close(attr->parent_err);
385 dup2(attr->child_err->filedes, STDERR_FILENO);
386 apr_file_close(attr->child_err);
389 apr_signal(SIGCHLD, SIG_DFL); /* not sure if this is needed or not */
391 if (attr->currdir != NULL) {
392 if (chdir(attr->currdir) == -1) {
394 attr->errfn(pool, errno, "change of working directory failed");
396 exit(-1); /* We have big problems, the child should exit. */
400 if ((status = limit_proc(attr)) != APR_SUCCESS) {
402 attr->errfn(pool, errno, "setting of resource limits failed");
404 exit(-1); /* We have big problems, the child should exit. */
407 if (attr->cmdtype == APR_SHELLCMD ||
408 attr->cmdtype == APR_SHELLCMD_ENV) {
410 const char *newargs[4];
412 newargs[0] = SHELL_PATH;
417 onearg_len += strlen(args[i]);
418 onearg_len++; /* for space delimiter */
424 /* bad parameters; we're doomed */
427 /* no args, or caller already built a single string from
430 newargs[2] = args[0];
436 ch = onearg = apr_palloc(pool, onearg_len);
439 size_t len = strlen(args[i]);
441 memcpy(ch, args[i], len);
447 --ch; /* back up to trailing blank */
455 if (attr->detached) {
456 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
459 if (attr->cmdtype == APR_SHELLCMD) {
460 execve(SHELL_PATH, (char * const *) newargs, (char * const *)env);
463 execv(SHELL_PATH, (char * const *)newargs);
466 else if (attr->cmdtype == APR_PROGRAM) {
467 if (attr->detached) {
468 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
471 execve(progname, (char * const *)args, (char * const *)env);
473 else if (attr->cmdtype == APR_PROGRAM_ENV) {
474 if (attr->detached) {
475 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
478 execv(progname, (char * const *)args);
481 /* APR_PROGRAM_PATH */
482 if (attr->detached) {
483 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
486 execvp(progname, (char * const *)args);
491 desc = apr_psprintf(pool, "exec of '%s' failed",
493 attr->errfn(pool, errno, desc);
496 exit(-1); /* if we get here, there is a problem, so exit with an
501 if (attr->child_in) {
502 apr_file_close(attr->child_in);
505 if (attr->child_out) {
506 apr_file_close(attr->child_out);
509 if (attr->child_err) {
510 apr_file_close(attr->child_err);
516 APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc,
518 apr_exit_why_e *exitwhy,
519 apr_wait_how_e waithow,
523 return apr_proc_wait(proc, exitcode, exitwhy, waithow);
526 APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc,
527 int *exitcode, apr_exit_why_e *exitwhy,
528 apr_wait_how_e waithow)
531 int waitpid_options = WUNTRACED;
534 apr_exit_why_e ignorewhy;
536 if (exitcode == NULL) {
540 if (exitwhy == NULL) {
541 exitwhy = &ignorewhy;
544 if (waithow != APR_WAIT) {
545 waitpid_options |= WNOHANG;
549 pstatus = waitpid(proc->pid, &exit_int, waitpid_options);
550 } while (pstatus < 0 && errno == EINTR);
555 if (WIFEXITED(exit_int)) {
556 *exitwhy = APR_PROC_EXIT;
557 *exitcode = WEXITSTATUS(exit_int);
559 else if (WIFSIGNALED(exit_int)) {
560 *exitwhy = APR_PROC_SIGNAL;
563 if (WCOREDUMP(exit_int)) {
564 *exitwhy |= APR_PROC_SIGNAL_CORE;
568 *exitcode = WTERMSIG(exit_int);
571 /* unexpected condition */
575 return APR_CHILD_DONE;
577 else if (pstatus == 0) {
578 return APR_CHILD_NOTDONE;
584 APR_DECLARE(apr_status_t) apr_procattr_limit_set(apr_procattr_t *attr,
586 struct rlimit *limit)
591 attr->limit_cpu = limit;
598 #if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)
599 attr->limit_mem = limit;
605 case APR_LIMIT_NPROC:
607 attr->limit_nproc = limit;
613 case APR_LIMIT_NOFILE:
615 attr->limit_nofile = limit;