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