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