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#define INCL_DOS
18#define INCL_DOSERRORS
19
20#include "apr_arch_threadproc.h"
21#include "apr_arch_file_io.h"
22#include "apr_private.h"
23#include "apr_thread_proc.h"
24#include "apr_file_io.h"
25#include "apr_general.h"
26#include "apr_lib.h"
27#include "apr_portable.h"
28#include "apr_strings.h"
29#include "apr_signal.h"
30#include <signal.h>
31#include <string.h>
32#include <sys/wait.h>
33#include <unistd.h>
34#include <process.h>
35#include <stdlib.h>
36
37/* Heavy on no'ops, here's what we want to pass if there is APR_NO_FILE
38 * requested for a specific child handle;
39 */
40static apr_file_t no_file = { NULL, -1, };
41
42APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new, apr_pool_t *pool)
43{
44    (*new) = (apr_procattr_t *)apr_palloc(pool,
45              sizeof(apr_procattr_t));
46
47    if ((*new) == NULL) {
48        return APR_ENOMEM;
49    }
50    (*new)->pool = pool;
51    (*new)->parent_in = NULL;
52    (*new)->child_in = NULL;
53    (*new)->parent_out = NULL;
54    (*new)->child_out = NULL;
55    (*new)->parent_err = NULL;
56    (*new)->child_err = NULL;
57    (*new)->currdir = NULL;
58    (*new)->cmdtype = APR_PROGRAM;
59    (*new)->detached = FALSE;
60    return APR_SUCCESS;
61}
62
63APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr,
64                                              apr_int32_t in,
65                                              apr_int32_t out,
66                                              apr_int32_t err)
67{
68    apr_status_t rv;
69
70    if ((in != APR_NO_PIPE) && (in != APR_NO_FILE)) {
71        /* APR_CHILD_BLOCK maps to APR_WRITE_BLOCK, while
72         * APR_PARENT_BLOCK maps to APR_READ_BLOCK, so transpose
73         * the CHILD/PARENT blocking flags for the stdin pipe.
74         * stdout/stderr map to the correct mode by default.
75         */
76        if (in == APR_CHILD_BLOCK)
77            in = APR_READ_BLOCK;
78        else if (in == APR_PARENT_BLOCK)
79            in = APR_WRITE_BLOCK;
80
81        if ((rv = apr_file_pipe_create_ex(&attr->child_in, &attr->parent_in,
82                                          in, attr->pool)) == APR_SUCCESS)
83            rv = apr_file_inherit_unset(attr->parent_in);
84        if (rv != APR_SUCCESS)
85            return rv;
86    }
87    else if (in == APR_NO_FILE)
88        attr->child_in = &no_file;
89
90    if ((out != APR_NO_PIPE) && (out != APR_NO_FILE)) {
91        if ((rv = apr_file_pipe_create_ex(&attr->parent_out, &attr->child_out,
92                                          out, attr->pool)) == APR_SUCCESS)
93            rv = apr_file_inherit_unset(attr->parent_out);
94        if (rv != APR_SUCCESS)
95            return rv;
96    }
97    else if (out == APR_NO_FILE)
98        attr->child_out = &no_file;
99
100    if ((err != APR_NO_PIPE) && (err != APR_NO_FILE)) {
101        if ((rv = apr_file_pipe_create_ex(&attr->parent_err, &attr->child_err,
102                                          err, attr->pool)) == APR_SUCCESS)
103            rv = apr_file_inherit_unset(attr->parent_err);
104        if (rv != APR_SUCCESS)
105            return rv;
106    }
107    else if (err == APR_NO_FILE)
108        attr->child_err = &no_file;
109
110    return APR_SUCCESS;
111}
112
113APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, apr_file_t *child_in,
114                                   apr_file_t *parent_in)
115{
116    apr_status_t rv;
117
118    if (attr->child_in == NULL && attr->parent_in == NULL
119            && child_in == NULL && parent_in == NULL)
120        if ((rv = apr_file_pipe_create(&attr->child_in, &attr->parent_in,
121                                       attr->pool)) == APR_SUCCESS)
122            rv = apr_file_inherit_unset(attr->parent_in);
123
124    if (child_in != NULL && rv == APR_SUCCESS) {
125        if (attr->child_in && (attr->child_in->filedes != -1))
126            rv = apr_file_dup2(attr->child_in, child_in, attr->pool);
127        else {
128            attr->child_in = NULL;
129            if ((rv = apr_file_dup(&attr->child_in, child_in, attr->pool))
130                    == APR_SUCCESS)
131                rv = apr_file_inherit_set(attr->child_in);
132        }
133    }
134
135    if (parent_in != NULL && rv == APR_SUCCESS) {
136        rv = apr_file_dup(&attr->parent_in, parent_in, attr->pool);
137    }
138
139    return rv;
140}
141
142APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, apr_file_t *child_out,
143                                                     apr_file_t *parent_out)
144{
145    apr_status_t rv;
146
147    if (attr->child_out == NULL && attr->parent_out == NULL
148           && child_out == NULL && parent_out == NULL)
149        if ((rv = apr_file_pipe_create(&attr->parent_out, &attr->child_out,
150                                       attr->pool)) == APR_SUCCESS)
151            rv = apr_file_inherit_unset(attr->parent_out);
152
153    if (child_out != NULL && rv == APR_SUCCESS) {
154        if (attr->child_out && (attr->child_out->filedes != -1))
155            rv = apr_file_dup2(attr->child_out, child_out, attr->pool);
156        else {
157            attr->child_out = NULL;
158            if ((rv = apr_file_dup(&attr->child_out, child_out, attr->pool))
159                    == APR_SUCCESS)
160                rv = apr_file_inherit_set(attr->child_out);
161        }
162    }
163
164    if (parent_out != NULL && rv == APR_SUCCESS) {
165        rv = apr_file_dup(&attr->parent_out, parent_out, attr->pool);
166    }
167
168    return rv;
169}
170
171APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, apr_file_t *child_err,
172                                                     apr_file_t *parent_err)
173{
174    apr_status_t rv;
175
176    if (attr->child_err == NULL && attr->parent_err == NULL
177           && child_err == NULL && parent_err == NULL)
178        if ((rv = apr_file_pipe_create(&attr->parent_err, &attr->child_err,
179                                       attr->pool)) == APR_SUCCESS)
180            rv = apr_file_inherit_unset(attr->parent_err);
181
182    if (child_err != NULL && rv == APR_SUCCESS) {
183        if (attr->child_err && (attr->child_err->filedes != -1))
184            rv = apr_file_dup2(attr->child_err, child_err, attr->pool);
185        else {
186            attr->child_err = NULL;
187            if ((rv = apr_file_dup(&attr->child_err, child_err, attr->pool))
188                    == APR_SUCCESS)
189                rv = apr_file_inherit_set(attr->child_err);
190        }
191    }
192
193    if (parent_err != NULL && rv == APR_SUCCESS) {
194        rv = apr_file_dup(&attr->parent_err, parent_err, attr->pool);
195    }
196
197    return rv;
198}
199
200APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, const char *dir)
201{
202    attr->currdir = apr_pstrdup(attr->pool, dir);
203    if (attr->currdir) {
204        return APR_SUCCESS;
205    }
206    return APR_ENOMEM;
207}
208
209APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr,
210                                                   apr_cmdtype_e cmd)
211{
212    attr->cmdtype = cmd;
213    return APR_SUCCESS;
214}
215
216APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, apr_int32_t detach)
217{
218    attr->detached = detach;
219    return APR_SUCCESS;
220}
221
222APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool)
223{
224    int pid;
225
226    if ((pid = fork()) < 0) {
227        return errno;
228    }
229    else if (pid == 0) {
230        proc->pid = pid;
231        proc->in = NULL;
232        proc->out = NULL;
233        proc->err = NULL;
234        return APR_INCHILD;
235    }
236    proc->pid = pid;
237    proc->in = NULL;
238    proc->out = NULL;
239    proc->err = NULL;
240    return APR_INPARENT;
241}
242
243
244
245/* quotes in the string are doubled up.
246 * Used to escape quotes in args passed to OS/2's cmd.exe
247 */
248static char *double_quotes(apr_pool_t *pool, const char *str)
249{
250    int num_quotes = 0;
251    int len = 0;
252    char *quote_doubled_str, *dest;
253
254    while (str[len]) {
255        num_quotes += str[len++] == '\"';
256    }
257
258    quote_doubled_str = apr_palloc(pool, len + num_quotes + 1);
259    dest = quote_doubled_str;
260
261    while (*str) {
262        if (*str == '\"')
263            *(dest++) = '\"';
264        *(dest++) = *(str++);
265    }
266
267    *dest = 0;
268    return quote_doubled_str;
269}
270
271
272
273APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr,
274                                                       apr_child_errfn_t *errfn)
275{
276    /* won't ever be called on this platform, so don't save the function pointer */
277    return APR_SUCCESS;
278}
279
280
281
282APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr,
283                                                       apr_int32_t chk)
284{
285    /* won't ever be used on this platform, so don't save the flag */
286    return APR_SUCCESS;
287}
288
289APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr,
290                                                       apr_int32_t addrspace)
291{
292    /* won't ever be used on this platform, so don't save the flag */
293    return APR_SUCCESS;
294}
295
296
297
298APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *proc, const char *progname,
299                                          const char * const *args,
300                                          const char * const *env,
301                                          apr_procattr_t *attr, apr_pool_t *pool)
302{
303    int i, arg, numargs, cmdlen;
304    apr_status_t status;
305    const char **newargs;
306    char savedir[300];
307    HFILE save_in, save_out, save_err, dup;
308    int criticalsection = FALSE;
309    char *extension, *newprogname, *extra_arg = NULL, *cmdline, *cmdline_pos;
310    char interpreter[1024];
311    char error_object[260];
312    apr_file_t *progfile;
313    int env_len, e;
314    char *env_block, *env_block_pos;
315    RESULTCODES rescodes;
316
317    proc->in = attr->parent_in;
318    proc->err = attr->parent_err;
319    proc->out = attr->parent_out;
320
321    /* Prevent other threads from running while these process-wide resources are modified */
322    if (attr->child_in || attr->child_out || attr->child_err || attr->currdir) {
323        criticalsection = TRUE;
324        DosEnterCritSec();
325    }
326
327    if (attr->child_in) {
328        save_in = -1;
329        DosDupHandle(STDIN_FILENO, &save_in);
330        dup = STDIN_FILENO;
331        if (attr->child_in->filedes == -1)
332            DosClose(dup);
333        else
334            DosDupHandle(attr->child_in->filedes, &dup);
335    }
336
337    if (attr->child_out) {
338        save_out = -1;
339        DosDupHandle(STDOUT_FILENO, &save_out);
340        dup = STDOUT_FILENO;
341        if (attr->child_out->filedes == -1)
342            DosClose(dup);
343        else
344            DosDupHandle(attr->child_out->filedes, &dup);
345    }
346
347    if (attr->child_err) {
348        save_err = -1;
349        DosDupHandle(STDERR_FILENO, &save_err);
350        dup = STDERR_FILENO;
351        if (attr->child_err->filedes == -1)
352            DosClose(dup);
353        else
354            DosDupHandle(attr->child_err->filedes, &dup);
355    }
356
357    apr_signal(SIGCHLD, SIG_DFL); /*not sure if this is needed or not */
358
359    if (attr->currdir != NULL) {
360        _getcwd2(savedir, sizeof(savedir));
361
362        if (_chdir2(attr->currdir) < 0) {
363            if (criticalsection)
364                DosExitCritSec();
365            return errno;
366        }
367    }
368
369    interpreter[0] = 0;
370    extension = strrchr(progname, '.');
371
372    if (extension == NULL || strchr(extension, '/') || strchr(extension, '\\'))
373        extension = "";
374
375    /* ### how to handle APR_PROGRAM_ENV and APR_PROGRAM_PATH? */
376
377    if (attr->cmdtype == APR_SHELLCMD ||
378        attr->cmdtype == APR_SHELLCMD_ENV ||
379        strcasecmp(extension, ".cmd") == 0) {
380        strcpy(interpreter, "#!" SHELL_PATH);
381        extra_arg = "/C";
382    } else if (stricmp(extension, ".exe") != 0) {
383        status = apr_file_open(&progfile, progname, APR_READ|APR_BUFFERED, 0, pool);
384
385        if (status != APR_SUCCESS && APR_STATUS_IS_ENOENT(status)) {
386            progname = apr_pstrcat(pool, progname, ".exe", NULL);
387        }
388
389        if (status == APR_SUCCESS) {
390            status = apr_file_gets(interpreter, sizeof(interpreter), progfile);
391
392            if (status == APR_SUCCESS) {
393                if (interpreter[0] == '#' && interpreter[1] == '!') {
394                    /* delete CR/LF & any other whitespace off the end */
395                    int end = strlen(interpreter) - 1;
396
397                    while (end >= 0 && apr_isspace(interpreter[end])) {
398                        interpreter[end] = '\0';
399                        end--;
400                    }
401
402                    if (interpreter[2] != '/' && interpreter[2] != '\\' && interpreter[3] != ':') {
403                        char buffer[300];
404
405                        if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) {
406                            strcpy(interpreter+2, buffer);
407                        } else {
408                            strcat(interpreter, ".exe");
409                            if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) {
410                                strcpy(interpreter+2, buffer);
411                            }
412                        }
413                    }
414                } else {
415                    interpreter[0] = 0;
416                }
417            }
418
419            apr_file_close(progfile);
420        }
421    }
422
423    i = 0;
424
425    while (args && args[i]) {
426        i++;
427    }
428
429    newargs = (const char **)apr_palloc(pool, sizeof (char *) * (i + 4));
430    numargs = 0;
431
432    if (interpreter[0])
433        newargs[numargs++] = interpreter + 2;
434    if (extra_arg)
435        newargs[numargs++] = "/c";
436
437    newargs[numargs++] = newprogname = apr_pstrdup(pool, progname);
438    arg = 1;
439
440    while (args && args[arg]) {
441        newargs[numargs++] = args[arg++];
442    }
443
444    newargs[numargs] = NULL;
445
446    for (i=0; newprogname[i]; i++)
447        if (newprogname[i] == '/')
448            newprogname[i] = '\\';
449
450    cmdlen = 0;
451
452    for (i=0; i<numargs; i++)
453        cmdlen += strlen(newargs[i]) + 3;
454
455    cmdline = apr_palloc(pool, cmdlen + 2);
456    cmdline_pos = cmdline;
457
458    for (i=0; i<numargs; i++) {
459        const char *a = newargs[i];
460
461        if (strpbrk(a, "&|<>\" "))
462            a = apr_pstrcat(pool, "\"", double_quotes(pool, a), "\"", NULL);
463
464        if (i)
465            *(cmdline_pos++) = ' ';
466
467        strcpy(cmdline_pos, a);
468        cmdline_pos += strlen(cmdline_pos);
469    }
470
471    *(++cmdline_pos) = 0; /* Add required second terminator */
472    cmdline_pos = strchr(cmdline, ' ');
473
474    if (cmdline_pos) {
475        *cmdline_pos = 0;
476        cmdline_pos++;
477    }
478
479    /* Create environment block from list of envariables */
480    if (env) {
481        for (env_len=1, e=0; env[e]; e++)
482            env_len += strlen(env[e]) + 1;
483
484        env_block = apr_palloc(pool, env_len);
485        env_block_pos = env_block;
486
487        for (e=0; env[e]; e++) {
488            strcpy(env_block_pos, env[e]);
489            env_block_pos += strlen(env_block_pos) + 1;
490        }
491
492        *env_block_pos = 0; /* environment block is terminated by a double null */
493    } else
494        env_block = NULL;
495
496    status = DosExecPgm(error_object, sizeof(error_object),
497                        attr->detached ? EXEC_BACKGROUND : EXEC_ASYNCRESULT,
498                        cmdline, env_block, &rescodes, cmdline);
499
500    proc->pid = rescodes.codeTerminate;
501
502    if (attr->currdir != NULL) {
503        chdir(savedir);
504    }
505
506    if (attr->child_in) {
507        if (attr->child_in->filedes != -1) {
508            apr_file_close(attr->child_in);
509        }
510
511        dup = STDIN_FILENO;
512        DosDupHandle(save_in, &dup);
513        DosClose(save_in);
514    }
515
516    if (attr->child_out) {
517        if  (attr->child_out->filedes != -1) {
518            apr_file_close(attr->child_out);
519        }
520
521        dup = STDOUT_FILENO;
522        DosDupHandle(save_out, &dup);
523        DosClose(save_out);
524    }
525
526    if (attr->child_err) {
527        if (attr->child_err->filedes != -1) {
528            apr_file_close(attr->child_err);
529        }
530
531        dup = STDERR_FILENO;
532        DosDupHandle(save_err, &dup);
533        DosClose(save_err);
534    }
535
536    if (criticalsection)
537        DosExitCritSec();
538
539    return status;
540}
541
542
543
544static void proces_result_codes(RESULTCODES codes,
545                                int *exitcode,
546                                apr_exit_why_e *exitwhy)
547{
548    int result = 0;
549    apr_exit_why_e why = APR_PROC_EXIT;
550
551    switch (codes.codeTerminate) {
552    case TC_EXIT:        /* Normal exit */
553        why = APR_PROC_EXIT;
554        result = codes.codeResult;
555        break;
556
557    case TC_HARDERROR:   /* Hard error halt */
558        why = APR_PROC_SIGNAL;
559        result = SIGSYS;
560        break;
561
562    case TC_KILLPROCESS: /* Was killed by a DosKillProcess() */
563        why = APR_PROC_SIGNAL;
564        result = SIGKILL;
565        break;
566
567    case TC_TRAP:        /* TRAP in 16 bit code */
568    case TC_EXCEPTION:   /* Threw an exception (32 bit code) */
569        why = APR_PROC_SIGNAL;
570
571        switch (codes.codeResult | XCPT_FATAL_EXCEPTION) {
572        case XCPT_ACCESS_VIOLATION:
573            result = SIGSEGV;
574            break;
575
576        case XCPT_ILLEGAL_INSTRUCTION:
577            result = SIGILL;
578            break;
579
580        case XCPT_FLOAT_DIVIDE_BY_ZERO:
581        case XCPT_INTEGER_DIVIDE_BY_ZERO:
582            result = SIGFPE;
583            break;
584
585        default:
586            result = codes.codeResult;
587            break;
588        }
589    }
590
591    if (exitcode) {
592        *exitcode = result;
593    }
594
595    if (exitwhy) {
596        *exitwhy = why;
597    }
598}
599
600
601
602APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc,
603                                                  int *exitcode,
604                                                  apr_exit_why_e *exitwhy,
605                                                  apr_wait_how_e waithow,
606                                                  apr_pool_t *p)
607{
608    RESULTCODES codes;
609    ULONG rc;
610    PID pid;
611
612    rc = DosWaitChild(DCWA_PROCESSTREE, waithow == APR_WAIT ? DCWW_WAIT : DCWW_NOWAIT, &codes, &pid, 0);
613
614    if (rc == 0) {
615        proc->pid = pid;
616        proces_result_codes(codes, exitcode, exitwhy);
617        return APR_CHILD_DONE;
618    } else if (rc == ERROR_CHILD_NOT_COMPLETE) {
619        return APR_CHILD_NOTDONE;
620    }
621
622    return APR_OS2_STATUS(rc);
623}
624
625
626
627APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc,
628                                        int *exitcode, apr_exit_why_e *exitwhy,
629                                        apr_wait_how_e waithow)
630{
631    RESULTCODES codes;
632    ULONG rc;
633    PID pid;
634    rc = DosWaitChild(DCWA_PROCESS, waithow == APR_WAIT ? DCWW_WAIT : DCWW_NOWAIT, &codes, &pid, proc->pid);
635
636    if (rc == 0) {
637        proces_result_codes(codes, exitcode, exitwhy);
638        return APR_CHILD_DONE;
639    } else if (rc == ERROR_CHILD_NOT_COMPLETE) {
640        return APR_CHILD_NOTDONE;
641    }
642
643    return APR_OS2_STATUS(rc);
644}
645
646
647
648APR_DECLARE(apr_status_t) apr_proc_detach(int daemonize)
649{
650    return APR_ENOTIMPL;
651}
652
653APR_DECLARE(apr_status_t) apr_procattr_user_set(apr_procattr_t *attr,
654                                                const char *username,
655                                                const char *password)
656{
657    return APR_ENOTIMPL;
658}
659
660APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr,
661                                                 const char *groupname)
662{
663    return APR_ENOTIMPL;
664}
665