upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / srclib / apr / threadproc / unix / proc.c
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
22 APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new,
23                                               apr_pool_t *pool)
24 {
25     (*new) = (apr_procattr_t *)apr_pcalloc(pool, sizeof(apr_procattr_t));
26
27     if ((*new) == NULL) {
28         return APR_ENOMEM;
29     }
30     (*new)->pool = pool;
31     (*new)->cmdtype = APR_PROGRAM;
32     return APR_SUCCESS;
33 }
34
35 APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr,
36                                               apr_int32_t in,
37                                               apr_int32_t out,
38                                               apr_int32_t err)
39 {
40     apr_status_t status;
41     if (in != 0) {
42         if ((status = apr_file_pipe_create(&attr->child_in, &attr->parent_in,
43                                            attr->pool)) != APR_SUCCESS) {
44             return status;
45         }
46
47         switch (in) {
48         case APR_FULL_BLOCK:
49             break;
50         case APR_PARENT_BLOCK:
51             apr_file_pipe_timeout_set(attr->child_in, 0);
52             break;
53         case APR_CHILD_BLOCK:
54             apr_file_pipe_timeout_set(attr->parent_in, 0);
55             break;
56         default:
57             apr_file_pipe_timeout_set(attr->child_in, 0);
58             apr_file_pipe_timeout_set(attr->parent_in, 0);
59         }
60     }
61
62     if (out) {
63         if ((status = apr_file_pipe_create(&attr->parent_out, &attr->child_out,
64                                            attr->pool)) != APR_SUCCESS) {
65             return status;
66         }
67
68         switch (out) {
69         case APR_FULL_BLOCK:
70             break;
71         case APR_PARENT_BLOCK:
72             apr_file_pipe_timeout_set(attr->child_out, 0);
73             break;
74         case APR_CHILD_BLOCK:
75             apr_file_pipe_timeout_set(attr->parent_out, 0);
76             break;
77         default:
78             apr_file_pipe_timeout_set(attr->child_out, 0);
79             apr_file_pipe_timeout_set(attr->parent_out, 0);
80         }
81     }
82
83     if (err) {
84         if ((status = apr_file_pipe_create(&attr->parent_err, &attr->child_err,
85                                            attr->pool)) != APR_SUCCESS) {
86             return status;
87         }
88
89         switch (err) {
90         case APR_FULL_BLOCK:
91             break;
92         case APR_PARENT_BLOCK:
93             apr_file_pipe_timeout_set(attr->child_err, 0);
94             break;
95         case APR_CHILD_BLOCK:
96             apr_file_pipe_timeout_set(attr->parent_err, 0);
97             break;
98         default:
99             apr_file_pipe_timeout_set(attr->child_err, 0);
100             apr_file_pipe_timeout_set(attr->parent_err, 0);
101         }
102     }
103
104     return APR_SUCCESS;
105 }
106
107
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)
111 {
112     apr_status_t rv = APR_SUCCESS;
113
114     if (attr->child_in == NULL && attr->parent_in == NULL)
115         rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in, attr->pool);
116     
117     if (child_in != NULL && rv == APR_SUCCESS)
118         rv = apr_file_dup2(attr->child_in, child_in, attr->pool);
119
120     if (parent_in != NULL && rv == APR_SUCCESS)
121         rv = apr_file_dup2(attr->parent_in, parent_in, attr->pool);
122
123     return rv;
124 }
125
126
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)
130 {
131     apr_status_t rv = APR_SUCCESS;
132
133     if (attr->child_out == NULL && attr->parent_out == NULL)
134         rv = apr_file_pipe_create(&attr->child_out, &attr->parent_out, attr->pool);
135
136     if (child_out != NULL && rv == APR_SUCCESS)
137         rv = apr_file_dup2(attr->child_out, child_out, attr->pool);
138
139     if (parent_out != NULL && rv == APR_SUCCESS)
140         rv = apr_file_dup2(attr->parent_out, parent_out, attr->pool);
141
142     return rv;
143 }
144
145
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)
149 {
150     apr_status_t rv = APR_SUCCESS;
151
152     if (attr->child_err == NULL && attr->parent_err == NULL)
153         rv = apr_file_pipe_create(&attr->child_err, &attr->parent_err, attr->pool);
154
155     if (child_err != NULL && rv == APR_SUCCESS)
156         rv = apr_file_dup2(attr->child_err, child_err, attr->pool);
157
158     if (parent_err != NULL && rv == APR_SUCCESS)
159         rv = apr_file_dup2(attr->parent_err, parent_err, attr->pool);
160
161     return rv;
162 }
163
164
165 APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr,
166                                                const char *dir)
167 {
168     attr->currdir = apr_pstrdup(attr->pool, dir);
169     if (attr->currdir) {
170         return APR_SUCCESS;
171     }
172
173     return APR_ENOMEM;
174 }
175
176 APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr,
177                                                    apr_cmdtype_e cmd)
178 {
179     attr->cmdtype = cmd;
180     return APR_SUCCESS;
181 }
182
183 APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr,
184                                                   apr_int32_t detach)
185 {
186     attr->detached = detach;
187     return APR_SUCCESS;
188 }
189
190 APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool)
191 {
192     int pid;
193
194     if ((pid = fork()) < 0) {
195         return errno;
196     }
197     else if (pid == 0) {
198         proc->pid = pid;
199         proc->in = NULL;
200         proc->out = NULL;
201         proc->err = NULL;
202
203         return APR_INCHILD;
204     }
205
206     proc->pid = pid;
207     proc->in = NULL;
208     proc->out = NULL;
209     proc->err = NULL;
210
211     return APR_INPARENT;
212 }
213
214 static apr_status_t limit_proc(apr_procattr_t *attr)
215 {
216 #if APR_HAVE_STRUCT_RLIMIT && APR_HAVE_SETRLIMIT
217 #ifdef RLIMIT_CPU
218     if (attr->limit_cpu != NULL) {
219         if ((setrlimit(RLIMIT_CPU, attr->limit_cpu)) != 0) {
220             return errno;
221         }
222     }
223 #endif
224 #ifdef RLIMIT_NPROC
225     if (attr->limit_nproc != NULL) {
226         if ((setrlimit(RLIMIT_NPROC, attr->limit_nproc)) != 0) {
227             return errno;
228         }
229     }
230 #endif
231 #ifdef RLIMIT_NOFILE
232     if (attr->limit_nofile != NULL) {
233         if ((setrlimit(RLIMIT_NOFILE, attr->limit_nofile)) != 0) {
234             return errno;
235         }
236     }
237 #endif
238 #if defined(RLIMIT_AS)
239     if (attr->limit_mem != NULL) {
240         if ((setrlimit(RLIMIT_AS, attr->limit_mem)) != 0) {
241             return errno;
242         }
243     }
244 #elif defined(RLIMIT_DATA)
245     if (attr->limit_mem != NULL) {
246         if ((setrlimit(RLIMIT_DATA, attr->limit_mem)) != 0) {
247             return errno;
248         }
249     }
250 #elif defined(RLIMIT_VMEM)
251     if (attr->limit_mem != NULL) {
252         if ((setrlimit(RLIMIT_VMEM, attr->limit_mem)) != 0) {
253             return errno;
254         }
255     }
256 #endif
257 #else
258     /*
259      * Maybe make a note in error_log that setrlimit isn't supported??
260      */
261
262 #endif
263     return APR_SUCCESS;
264 }
265
266 APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr,
267                                                        apr_child_errfn_t *errfn)
268 {
269     attr->errfn = errfn;
270     return APR_SUCCESS;
271 }
272
273 APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr,
274                                                        apr_int32_t chk)
275 {
276     attr->errchk = chk;
277     return APR_SUCCESS;
278 }
279
280 APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr,
281                                                        apr_int32_t addrspace)
282 {
283     /* won't ever be used on this platform, so don't save the flag */
284     return APR_SUCCESS;
285 }
286
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,
292                                           apr_pool_t *pool)
293 {
294     int i;
295     const char * const empty_envp[] = {NULL};
296
297     if (!env) { /* Specs require an empty array instead of NULL;
298                  * Purify will trigger a failure, even if many
299                  * implementations don't.
300                  */
301         env = empty_envp;
302     }
303
304     new->in = attr->parent_in;
305     new->err = attr->parent_err;
306     new->out = attr->parent_out;
307
308     if (attr->errchk) {
309         if (attr->currdir) {
310             if (access(attr->currdir, X_OK) == -1) {
311                 /* chdir() in child wouldn't have worked */
312                 return errno;
313             }
314         }
315
316         if (attr->cmdtype == APR_PROGRAM ||
317             attr->cmdtype == APR_PROGRAM_ENV ||
318             *progname == '/') {
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
322              * values of cmdtype
323              */
324             if (access(progname, R_OK|X_OK) == -1) {
325                 /* exec*() in child wouldn't have worked */
326                 return errno;
327             }
328         }
329         else {
330             /* todo: search PATH for progname then try to access it */
331         }
332     }
333
334     if ((new->pid = fork()) < 0) {
335         return errno;
336     }
337     else if (new->pid == 0) {
338         int status;
339         /* child process */
340
341         /*
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
344          * mod_cgid.
345          *
346          * If we do exec cleanup after the dup2() calls, cleanup can accidentally
347          * close our pipes which replaced any files which previously had
348          * descriptors 0-2.
349          *
350          * The solution is to kill the cleanup for the pipes, then do
351          * exec cleanup, then do the dup2() calls.
352          */
353
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);
357         }
358
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);
362         }
363
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);
367         }
368
369         apr_pool_cleanup_for_exec();
370
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);
375         }
376
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);
381         }
382
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);
387         }
388
389         apr_signal(SIGCHLD, SIG_DFL); /* not sure if this is needed or not */
390
391         if (attr->currdir != NULL) {
392             if (chdir(attr->currdir) == -1) {
393                 if (attr->errfn) {
394                     attr->errfn(pool, errno, "change of working directory failed");
395                 }
396                 exit(-1);   /* We have big problems, the child should exit. */
397             }
398         }
399
400         if ((status = limit_proc(attr)) != APR_SUCCESS) {
401             if (attr->errfn) {
402                 attr->errfn(pool, errno, "setting of resource limits failed");
403             }
404             exit(-1);   /* We have big problems, the child should exit. */
405         }
406
407         if (attr->cmdtype == APR_SHELLCMD ||
408             attr->cmdtype == APR_SHELLCMD_ENV) {
409             int onearg_len = 0;
410             const char *newargs[4];
411
412             newargs[0] = SHELL_PATH;
413             newargs[1] = "-c";
414
415             i = 0;
416             while (args[i]) {
417                 onearg_len += strlen(args[i]);
418                 onearg_len++; /* for space delimiter */
419                 i++;
420             }
421
422             switch(i) {
423             case 0:
424                 /* bad parameters; we're doomed */
425                 break;
426             case 1:
427                 /* no args, or caller already built a single string from
428                  * progname and args
429                  */
430                 newargs[2] = args[0];
431                 break;
432             default:
433             {
434                 char *ch, *onearg;
435                 
436                 ch = onearg = apr_palloc(pool, onearg_len);
437                 i = 0;
438                 while (args[i]) {
439                     size_t len = strlen(args[i]);
440
441                     memcpy(ch, args[i], len);
442                     ch += len;
443                     *ch = ' ';
444                     ++ch;
445                     ++i;
446                 }
447                 --ch; /* back up to trailing blank */
448                 *ch = '\0';
449                 newargs[2] = onearg;
450             }
451             }
452
453             newargs[3] = NULL;
454
455             if (attr->detached) {
456                 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
457             }
458
459             if (attr->cmdtype == APR_SHELLCMD) {
460                 execve(SHELL_PATH, (char * const *) newargs, (char * const *)env);
461             }
462             else {
463                 execv(SHELL_PATH, (char * const *)newargs);
464             }
465         }
466         else if (attr->cmdtype == APR_PROGRAM) {
467             if (attr->detached) {
468                 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
469             }
470
471             execve(progname, (char * const *)args, (char * const *)env);
472         }
473         else if (attr->cmdtype == APR_PROGRAM_ENV) {
474             if (attr->detached) {
475                 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
476             }
477
478             execv(progname, (char * const *)args);
479         }
480         else {
481             /* APR_PROGRAM_PATH */
482             if (attr->detached) {
483                 apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
484             }
485
486             execvp(progname, (char * const *)args);
487         }
488         if (attr->errfn) {
489             char *desc;
490
491             desc = apr_psprintf(pool, "exec of '%s' failed",
492                                 progname);
493             attr->errfn(pool, errno, desc);
494         }
495
496         exit(-1);  /* if we get here, there is a problem, so exit with an
497                     * error code. */
498     }
499
500     /* Parent process */
501     if (attr->child_in) {
502         apr_file_close(attr->child_in);
503     }
504
505     if (attr->child_out) {
506         apr_file_close(attr->child_out);
507     }
508
509     if (attr->child_err) {
510         apr_file_close(attr->child_err);
511     }
512
513     return APR_SUCCESS;
514 }
515
516 APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc,
517                                                   int *exitcode,
518                                                   apr_exit_why_e *exitwhy,
519                                                   apr_wait_how_e waithow,
520                                                   apr_pool_t *p)
521 {
522     proc->pid = -1;
523     return apr_proc_wait(proc, exitcode, exitwhy, waithow);
524 }
525
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)
529 {
530     pid_t pstatus;
531     int waitpid_options = WUNTRACED;
532     int exit_int;
533     int ignore;
534     apr_exit_why_e ignorewhy;
535
536     if (exitcode == NULL) {
537         exitcode = &ignore;
538     }
539
540     if (exitwhy == NULL) {
541         exitwhy = &ignorewhy;
542     }
543
544     if (waithow != APR_WAIT) {
545         waitpid_options |= WNOHANG;
546     }
547
548     do {
549         pstatus = waitpid(proc->pid, &exit_int, waitpid_options);
550     } while (pstatus < 0 && errno == EINTR);
551
552     if (pstatus > 0) {
553         proc->pid = pstatus;
554
555         if (WIFEXITED(exit_int)) {
556             *exitwhy = APR_PROC_EXIT;
557             *exitcode = WEXITSTATUS(exit_int);
558         }
559         else if (WIFSIGNALED(exit_int)) {
560             *exitwhy = APR_PROC_SIGNAL;
561
562 #ifdef WCOREDUMP
563             if (WCOREDUMP(exit_int)) {
564                 *exitwhy |= APR_PROC_SIGNAL_CORE;
565             }
566 #endif
567
568             *exitcode = WTERMSIG(exit_int);
569         }
570         else {
571             /* unexpected condition */
572             return APR_EGENERAL;
573         }
574
575         return APR_CHILD_DONE;
576     }
577     else if (pstatus == 0) {
578         return APR_CHILD_NOTDONE;
579     }
580
581     return errno;
582 }
583
584 APR_DECLARE(apr_status_t) apr_procattr_limit_set(apr_procattr_t *attr,
585                                                  apr_int32_t what,
586                                                  struct rlimit *limit)
587 {
588     switch(what) {
589         case APR_LIMIT_CPU:
590 #ifdef RLIMIT_CPU
591             attr->limit_cpu = limit;
592             break;
593 #else
594             return APR_ENOTIMPL;
595 #endif
596
597         case APR_LIMIT_MEM:
598 #if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)
599             attr->limit_mem = limit;
600             break;
601 #else
602             return APR_ENOTIMPL;
603 #endif
604
605         case APR_LIMIT_NPROC:
606 #ifdef RLIMIT_NPROC
607             attr->limit_nproc = limit;
608             break;
609 #else
610             return APR_ENOTIMPL;
611 #endif
612
613         case APR_LIMIT_NOFILE:
614 #ifdef RLIMIT_NOFILE
615             attr->limit_nofile = limit;
616             break;
617 #else
618             return APR_ENOTIMPL;
619 #endif
620
621     }
622
623     return APR_SUCCESS;
624 }