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
20/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE
21 * requested for a specific child handle;
22 */
23static apr_file_t no_file = { NULL, -1, };
24
25struct send_pipe {
26	int in;
27	int out;
28	int err;
29};
30
31APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new, apr_pool_t *pool)
32{
33    (*new) = (apr_procattr_t *)apr_palloc(pool,
34              sizeof(apr_procattr_t));
35
36    if ((*new) == NULL) {
37        return APR_ENOMEM;
38    }
39    (*new)->pool = pool;
40    (*new)->parent_in = NULL;
41    (*new)->child_in = NULL;
42    (*new)->parent_out = NULL;
43    (*new)->child_out = NULL;
44    (*new)->parent_err = NULL;
45    (*new)->child_err = NULL;
46    (*new)->currdir = NULL;
47    (*new)->cmdtype = APR_PROGRAM;
48    (*new)->detached = 0;
49    return APR_SUCCESS;
50}
51
52APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr,
53                                              apr_int32_t in,
54                                              apr_int32_t out,
55                                              apr_int32_t err)
56{
57    apr_status_t rv;
58
59    if ((in != APR_NO_PIPE) && (in != APR_NO_FILE)) {
60        /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while
61         * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose
62         * the CHILD/PARENT blocking flags for the stdin pipe.
63         * stdout/stderr map to the correct mode by default.
64         */
65        if (in == APR_CHILD_BLOCK)
66            in = APR_READ_BLOCK;
67        else if (in == APR_PARENT_BLOCK)
68            in = APR_WRITE_BLOCK;
69
70        if ((rv = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in,
71                                          in, attr->pool)) == APR_SUCCESS)
72            rv = apr_file_inherit_unset(attr->parent_in);
73        if (rv != APR_SUCCESS)
74            return rv;
75    }
76    else if (in == APR_NO_FILE)
77        attr->child_in = &no_file;
78
79    if ((out != APR_NO_PIPE) && (out != APR_NO_FILE)) {
80        if ((rv = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out,
81                                          out, attr->pool)) == APR_SUCCESS)
82            rv = apr_file_inherit_unset(attr->parent_out);
83        if (rv != APR_SUCCESS)
84            return rv;
85    }
86    else if (out == APR_NO_FILE)
87        attr->child_out = &no_file;
88
89    if ((err != APR_NO_PIPE) && (err != APR_NO_FILE)) {
90        if ((rv = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err,
91                                          err, attr->pool)) == APR_SUCCESS)
92            rv = apr_file_inherit_unset(attr->parent_err);
93        if (rv != APR_SUCCESS)
94            return rv;
95    }
96    else if (err == APR_NO_FILE)
97        attr->child_err = &no_file;
98
99    return APR_SUCCESS;
100}
101
102APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr,
103                                               const char *dir)
104{
105    char * cwd;
106    if (dir[0] != '/') {
107        cwd = (char*)malloc(sizeof(char) * PATH_MAX);
108        getcwd(cwd, PATH_MAX);
109        attr->currdir = (char *)apr_pstrcat(attr->pool, cwd, "/", dir, NULL);
110        free(cwd);
111    } else {
112        attr->currdir = (char *)apr_pstrdup(attr->pool, dir);
113    }
114    if (attr->currdir) {
115        return APR_SUCCESS;
116    }
117    return APR_ENOMEM;
118}
119
120APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr,
121                                                   apr_cmdtype_e cmd)
122{
123    attr->cmdtype = cmd;
124    return APR_SUCCESS;
125}
126
127APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, apr_int32_t detach)
128{
129    attr->detached = detach;
130    return APR_SUCCESS;
131}
132
133APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool)
134{
135    int pid;
136
137    if ((pid = fork()) < 0) {
138        return errno;
139    }
140    else if (pid == 0) {
141		/* This is really ugly...
142		 * The semantics of BeOS's fork() are that areas (used for shared
143		 * memory) get COW'd :-( The only way we can make shared memory
144		 * work across fork() is therefore to find any areas that have
145		 * been created and then clone them into our address space.
146         * Thankfully only COW'd areas have the lock variable set at
147         * anything but 0, so we can use that to find the areas we need to
148         * copy. Of course what makes it even worse is that the loop through
149         * the area's will go into an infinite loop, eating memory and then
150         * eventually segfault unless we know when we reach then end of the
151         * "original" areas and stop. Why? Well, we delete the area and then
152         * add another to the end of the list...
153		 */
154		area_info ai;
155		int32 cookie = 0;
156        area_id highest = 0;
157
158        while (get_next_area_info(0, &cookie, &ai) == B_OK)
159            if (ai.area	> highest)
160                highest = ai.area;
161        cookie = 0;
162        while (get_next_area_info(0, &cookie, &ai) == B_OK) {
163            if (ai.area > highest)
164                break;
165            if (ai.lock > 0) {
166                area_id original = find_area(ai.name);
167                delete_area(ai.area);
168                clone_area(ai.name, &ai.address, B_CLONE_ADDRESS,
169                           ai.protection, original);
170            }
171        }
172
173        proc->pid = pid;
174        proc->in = NULL;
175        proc->out = NULL;
176        proc->err = NULL;
177        return APR_INCHILD;
178    }
179    proc->pid = pid;
180    proc->in = NULL;
181    proc->out = NULL;
182    proc->err = NULL;
183    return APR_INPARENT;
184}
185
186APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr,
187                                                       apr_child_errfn_t *errfn)
188{
189    /* won't ever be called on this platform, so don't save the function pointer */
190    return APR_SUCCESS;
191}
192
193APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr,
194                                                       apr_int32_t chk)
195{
196    /* won't ever be used on this platform, so don't save the flag */
197    return APR_SUCCESS;
198}
199
200APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr,
201                                                       apr_int32_t addrspace)
202{
203    /* won't ever be used on this platform, so don't save the flag */
204    return APR_SUCCESS;
205}
206
207APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new, const char *progname,
208                                          const char * const *args,
209                                          const char * const *env,
210                                          apr_procattr_t *attr,
211                                          apr_pool_t *pool)
212{
213    int i=0,nargs=0;
214    char **newargs = NULL;
215    thread_id newproc, sender;
216    struct send_pipe *sp;
217    char * dir = NULL;
218
219    sp = (struct send_pipe *)apr_palloc(pool, sizeof(struct send_pipe));
220
221    new->in = attr->parent_in;
222    new->err = attr->parent_err;
223    new->out = attr->parent_out;
224    sp->in  = attr->child_in  ? attr->child_in->filedes  : FILENO_STDIN;
225    sp->out = attr->child_out ? attr->child_out->filedes : FILENO_STDOUT;
226    sp->err = attr->child_err ? attr->child_err->filedes : FILENO_STDERR;
227
228    i = 0;
229    while (args && args[i]) {
230        i++;
231    }
232
233	newargs = (char**)malloc(sizeof(char *) * (i + 4));
234	newargs[0] = strdup("/boot/home/config/bin/apr_proc_stub");
235    if (attr->currdir == NULL) {
236        /* we require the directory , so use a temp. variable */
237        dir = malloc(sizeof(char) * PATH_MAX);
238        getcwd(dir, PATH_MAX);
239        newargs[1] = strdup(dir);
240        free(dir);
241    } else {
242        newargs[1] = strdup(attr->currdir);
243    }
244    newargs[2] = strdup(progname);
245    i=0;nargs = 3;
246
247    while (args && args[i]) {
248        newargs[nargs] = strdup(args[i]);
249        i++;nargs++;
250    }
251    newargs[nargs] = NULL;
252
253    /* ### we should be looking at attr->cmdtype in here... */
254
255    newproc = load_image(nargs, (const char**)newargs, (const char**)env);
256
257    /* load_image copies the data so now we can free it... */
258    while (--nargs >= 0)
259        free (newargs[nargs]);
260    free(newargs);
261
262    if (newproc < B_NO_ERROR) {
263        return errno;
264    }
265
266    resume_thread(newproc);
267
268    if (attr->child_in && (attr->child_in->filedes != -1)) {
269        apr_file_close(attr->child_in);
270    }
271    if (attr->child_out && (attr->child_in->filedes != -1)) {
272        apr_file_close(attr->child_out);
273    }
274    if (attr->child_err && (attr->child_in->filedes != -1)) {
275        apr_file_close(attr->child_err);
276    }
277
278    send_data(newproc, 0, (void*)sp, sizeof(struct send_pipe));
279    new->pid = newproc;
280
281    /* before we go charging on we need the new process to get to a
282     * certain point.  When it gets there it'll let us know and we
283     * can carry on. */
284    receive_data(&sender, (void*)NULL,0);
285
286    return APR_SUCCESS;
287}
288
289APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc,
290                                                  int *exitcode,
291                                                  apr_exit_why_e *exitwhy,
292                                                  apr_wait_how_e waithow,
293                                                  apr_pool_t *p)
294{
295    proc->pid = -1;
296    return apr_proc_wait(proc, exitcode, exitwhy, waithow);
297}
298
299APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc,
300                                        int *exitcode,
301                                        apr_exit_why_e *exitwhy,
302                                        apr_wait_how_e waithow)
303{
304    pid_t pstatus;
305    int waitpid_options = WUNTRACED;
306    int exit_int;
307    int ignore;
308    apr_exit_why_e ignorewhy;
309
310    if (exitcode == NULL) {
311        exitcode = &ignore;
312    }
313    if (exitwhy == NULL) {
314        exitwhy = &ignorewhy;
315    }
316
317    if (waithow != APR_WAIT) {
318        waitpid_options |= WNOHANG;
319    }
320
321    if ((pstatus = waitpid(proc->pid, &exit_int, waitpid_options)) > 0) {
322        proc->pid = pstatus;
323        if (WIFEXITED(exit_int)) {
324            *exitwhy = APR_PROC_EXIT;
325            *exitcode = WEXITSTATUS(exit_int);
326        }
327        else if (WIFSIGNALED(exit_int)) {
328            *exitwhy = APR_PROC_SIGNAL;
329            *exitcode = WTERMSIG(exit_int);
330        }
331        else {
332
333            /* unexpected condition */
334            return APR_EGENERAL;
335        }
336        return APR_CHILD_DONE;
337    }
338    else if (pstatus == 0) {
339        return APR_CHILD_NOTDONE;
340    }
341
342    return errno;
343}
344
345APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, apr_file_t *child_in,
346                                   apr_file_t *parent_in)
347{
348    apr_status_t rv;
349
350    if (attr->child_in == NULL && attr->parent_in == NULL
351            && child_in == NULL && parent_in == NULL)
352        if ((rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in,
353                                       attr->pool)) == APR_SUCCESS)
354            rv = apr_file_inherit_unset(attr->parent_in);
355
356    if (child_in != NULL && rv == APR_SUCCESS) {
357        if (attr->child_in && (attr->child_in->filedes != -1))
358            rv = apr_file_dup2(attr->child_in, child_in, attr->pool);
359        else {
360            attr->child_in = NULL;
361            if ((rv = apr_file_dup(&attr->child_in, child_in, attr->pool))
362                    == APR_SUCCESS)
363                rv = apr_file_inherit_set(attr->child_in);
364        }
365    }
366
367    if (parent_in != NULL && rv == APR_SUCCESS)
368        rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool);
369
370    return rv;
371}
372
373APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, apr_file_t *child_out,
374                                                     apr_file_t *parent_out)
375{
376    apr_status_t rv;
377
378    if (attr->child_out == NULL && attr->parent_out == NULL
379           && child_out == NULL && parent_out == NULL)
380        if ((rv = apr_file_pipe_create(&attr->parent_out, &attr->child_out,
381                                       attr->pool)) == APR_SUCCESS)
382            rv = apr_file_inherit_unset(attr->parent_out);
383
384    if (child_out != NULL && rv == APR_SUCCESS) {
385        if (attr->child_out && (attr->child_out->filedes != -1))
386            rv = apr_file_dup2(attr->child_out, child_out, attr->pool);
387        else {
388            attr->child_out = NULL;
389            if ((rv = apr_file_dup(&attr->child_out, child_out, attr->pool))
390                    == APR_SUCCESS)
391                rv = apr_file_inherit_set(attr->child_out);
392        }
393    }
394
395    if (parent_out != NULL && rv == APR_SUCCESS)
396        rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool);
397
398    return rv;
399}
400
401APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, apr_file_t *child_err,
402                                                     apr_file_t *parent_err)
403{
404    apr_status_t rv;
405
406    if (attr->child_err == NULL && attr->parent_err == NULL
407           && child_err == NULL && parent_err == NULL)
408        if ((rv = apr_file_pipe_create(&attr->parent_err, &attr->child_err,
409                                       attr->pool)) == APR_SUCCESS)
410            rv = apr_file_inherit_unset(attr->parent_err);
411
412    if (child_err != NULL && rv == APR_SUCCESS) {
413        if (attr->child_err && (attr->child_err->filedes != -1))
414            rv = apr_file_dup2(attr->child_err, child_err, attr->pool);
415        else {
416            attr->child_err = NULL;
417            if ((rv = apr_file_dup(&attr->child_err, child_err, attr->pool))
418                    == APR_SUCCESS)
419                rv = apr_file_inherit_set(attr->child_err);
420        }
421    }
422
423    if (parent_err != NULL && rv == APR_SUCCESS)
424        rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool);
425
426    return rv;
427}
428
429APR_DECLARE(apr_status_t) apr_procattr_limit_set(apr_procattr_t *attr, apr_int32_t what,
430                                                  void *limit)
431{
432    return APR_ENOTIMPL;
433}
434
435APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr,
436                                                const char *username,
437                                                const char *password)
438{
439    return APR_ENOTIMPL;
440}
441
442APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr,
443                                                 const char *groupname)
444{
445    return APR_ENOTIMPL;
446}
447