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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #define INCL_DOSERRORS
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"
27 #include "apr_portable.h"
28 #include "apr_strings.h"
29 #include "apr_signal.h"
37 APR_DECLARE(apr_status_t) apr_procattr_create(apr_procattr_t **new, apr_pool_t *pool)
39 (*new) = (apr_procattr_t *)apr_palloc(pool,
40 sizeof(apr_procattr_t));
46 (*new)->parent_in = NULL;
47 (*new)->child_in = NULL;
48 (*new)->parent_out = NULL;
49 (*new)->child_out = NULL;
50 (*new)->parent_err = NULL;
51 (*new)->child_err = NULL;
52 (*new)->currdir = NULL;
53 (*new)->cmdtype = APR_PROGRAM;
54 (*new)->detached = FALSE;
58 APR_DECLARE(apr_status_t) apr_procattr_io_set(apr_procattr_t *attr, apr_int32_t in,
59 apr_int32_t out, apr_int32_t err)
63 if ((stat = apr_file_pipe_create(&attr->child_in, &attr->parent_in,
64 attr->pool)) != APR_SUCCESS) {
70 case APR_PARENT_BLOCK:
71 apr_file_pipe_timeout_set(attr->child_in, 0);
74 apr_file_pipe_timeout_set(attr->parent_in, 0);
77 apr_file_pipe_timeout_set(attr->child_in, 0);
78 apr_file_pipe_timeout_set(attr->parent_in, 0);
82 if ((stat = apr_file_pipe_create(&attr->parent_out, &attr->child_out,
83 attr->pool)) != APR_SUCCESS) {
89 case APR_PARENT_BLOCK:
90 apr_file_pipe_timeout_set(attr->child_out, 0);
93 apr_file_pipe_timeout_set(attr->parent_out, 0);
96 apr_file_pipe_timeout_set(attr->child_out, 0);
97 apr_file_pipe_timeout_set(attr->parent_out, 0);
101 if ((stat = apr_file_pipe_create(&attr->parent_err, &attr->child_err,
102 attr->pool)) != APR_SUCCESS) {
108 case APR_PARENT_BLOCK:
109 apr_file_pipe_timeout_set(attr->child_err, 0);
111 case APR_CHILD_BLOCK:
112 apr_file_pipe_timeout_set(attr->parent_err, 0);
115 apr_file_pipe_timeout_set(attr->child_err, 0);
116 apr_file_pipe_timeout_set(attr->parent_err, 0);
122 APR_DECLARE(apr_status_t) apr_procattr_child_in_set(apr_procattr_t *attr, apr_file_t *child_in,
123 apr_file_t *parent_in)
125 if (attr->child_in == NULL && attr->parent_in == NULL)
126 apr_file_pipe_create(&attr->child_in, &attr->parent_in, attr->pool);
128 if (child_in != NULL)
129 apr_file_dup(&attr->child_in, child_in, attr->pool);
131 if (parent_in != NULL)
132 apr_file_dup(&attr->parent_in, parent_in, attr->pool);
138 APR_DECLARE(apr_status_t) apr_procattr_child_out_set(apr_procattr_t *attr, apr_file_t *child_out,
139 apr_file_t *parent_out)
141 if (attr->child_out == NULL && attr->parent_out == NULL)
142 apr_file_pipe_create(&attr->child_out, &attr->parent_out, attr->pool);
144 if (child_out != NULL)
145 apr_file_dup(&attr->child_out, child_out, attr->pool);
147 if (parent_out != NULL)
148 apr_file_dup(&attr->parent_out, parent_out, attr->pool);
154 APR_DECLARE(apr_status_t) apr_procattr_child_err_set(apr_procattr_t *attr, apr_file_t *child_err,
155 apr_file_t *parent_err)
157 if (attr->child_err == NULL && attr->parent_err == NULL)
158 apr_file_pipe_create(&attr->child_err, &attr->parent_err, attr->pool);
160 if (child_err != NULL)
161 apr_file_dup(&attr->child_err, child_err, attr->pool);
163 if (parent_err != NULL)
164 apr_file_dup(&attr->parent_err, parent_err, attr->pool);
170 APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr, const char *dir)
172 attr->currdir = apr_pstrdup(attr->pool, dir);
179 APR_DECLARE(apr_status_t) apr_procattr_cmdtype_set(apr_procattr_t *attr,
186 APR_DECLARE(apr_status_t) apr_procattr_detach_set(apr_procattr_t *attr, apr_int32_t detach)
188 attr->detached = detach;
192 APR_DECLARE(apr_status_t) apr_proc_fork(apr_proc_t *proc, apr_pool_t *pool)
196 if ((pid = fork()) < 0) {
215 /* quotes in the string are doubled up.
216 * Used to escape quotes in args passed to OS/2's cmd.exe
218 static char *double_quotes(apr_pool_t *pool, const char *str)
222 char *quote_doubled_str, *dest;
225 num_quotes += str[len++] == '\"';
228 quote_doubled_str = apr_palloc(pool, len + num_quotes + 1);
229 dest = quote_doubled_str;
234 *(dest++) = *(str++);
238 return quote_doubled_str;
243 APR_DECLARE(apr_status_t) apr_procattr_child_errfn_set(apr_procattr_t *attr,
244 apr_child_errfn_t *errfn)
246 /* won't ever be called on this platform, so don't save the function pointer */
252 APR_DECLARE(apr_status_t) apr_procattr_error_check_set(apr_procattr_t *attr,
255 /* won't ever be used on this platform, so don't save the flag */
259 APR_DECLARE(apr_status_t) apr_procattr_addrspace_set(apr_procattr_t *attr,
260 apr_int32_t addrspace)
262 /* won't ever be used on this platform, so don't save the flag */
268 APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *proc, const char *progname,
269 const char * const *args,
270 const char * const *env,
271 apr_procattr_t *attr, apr_pool_t *pool)
273 int i, arg, numargs, cmdlen;
275 const char **newargs;
277 HFILE save_in, save_out, save_err, dup;
278 int criticalsection = FALSE;
279 char *extension, *newprogname, *extra_arg = NULL, *cmdline, *cmdline_pos;
280 char interpreter[1024];
281 char error_object[260];
282 apr_file_t *progfile;
284 char *env_block, *env_block_pos;
285 RESULTCODES rescodes;
287 /* Prevent other threads from running while these process-wide resources are modified */
288 if (attr->child_in || attr->child_out || attr->child_err || attr->currdir) {
289 criticalsection = TRUE;
293 if (attr->child_in) {
295 DosDupHandle(STDIN_FILENO, &save_in);
297 DosDupHandle(attr->child_in->filedes, &dup);
298 DosSetFHState(attr->parent_in->filedes, OPEN_FLAGS_NOINHERIT);
301 if (attr->child_out) {
303 DosDupHandle(STDOUT_FILENO, &save_out);
305 DosDupHandle(attr->child_out->filedes, &dup);
306 DosSetFHState(attr->parent_out->filedes, OPEN_FLAGS_NOINHERIT);
309 if (attr->child_err) {
311 DosDupHandle(STDERR_FILENO, &save_err);
313 DosDupHandle(attr->child_err->filedes, &dup);
314 DosSetFHState(attr->parent_err->filedes, OPEN_FLAGS_NOINHERIT);
317 apr_signal(SIGCHLD, SIG_DFL); /*not sure if this is needed or not */
319 if (attr->currdir != NULL) {
320 _getcwd2(savedir, sizeof(savedir));
322 if (_chdir2(attr->currdir) < 0) {
330 extension = strrchr(progname, '.');
332 if (extension == NULL || strchr(extension, '/') || strchr(extension, '\\'))
335 /* ### how to handle APR_PROGRAM_ENV and APR_PROGRAM_PATH? */
337 if (attr->cmdtype == APR_SHELLCMD ||
338 attr->cmdtype == APR_SHELLCMD_ENV ||
339 strcasecmp(extension, ".cmd") == 0) {
340 strcpy(interpreter, "#!" SHELL_PATH);
342 } else if (stricmp(extension, ".exe") != 0) {
343 status = apr_file_open(&progfile, progname, APR_READ|APR_BUFFERED, 0, pool);
345 if (status != APR_SUCCESS && APR_STATUS_IS_ENOENT(status)) {
346 progname = apr_pstrcat(pool, progname, ".exe", NULL);
349 if (status == APR_SUCCESS) {
350 status = apr_file_gets(interpreter, sizeof(interpreter), progfile);
352 if (status == APR_SUCCESS) {
353 if (interpreter[0] == '#' && interpreter[1] == '!') {
354 /* delete CR/LF & any other whitespace off the end */
355 int end = strlen(interpreter) - 1;
357 while (end >= 0 && apr_isspace(interpreter[end])) {
358 interpreter[end] = '\0';
362 if (interpreter[2] != '/' && interpreter[2] != '\\' && interpreter[3] != ':') {
365 if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) {
366 strcpy(interpreter+2, buffer);
368 strcat(interpreter, ".exe");
369 if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) {
370 strcpy(interpreter+2, buffer);
379 apr_file_close(progfile);
385 while (args && args[i]) {
389 newargs = (const char **)apr_palloc(pool, sizeof (char *) * (i + 4));
393 newargs[numargs++] = interpreter + 2;
395 newargs[numargs++] = "/c";
397 newargs[numargs++] = newprogname = apr_pstrdup(pool, progname);
400 while (args && args[arg]) {
401 newargs[numargs++] = args[arg++];
404 newargs[numargs] = NULL;
406 for (i=0; newprogname[i]; i++)
407 if (newprogname[i] == '/')
408 newprogname[i] = '\\';
412 for (i=0; i<numargs; i++)
413 cmdlen += strlen(newargs[i]) + 3;
415 cmdline = apr_palloc(pool, cmdlen + 2);
416 cmdline_pos = cmdline;
418 for (i=0; i<numargs; i++) {
419 const char *a = newargs[i];
421 if (strpbrk(a, "&|<>\" "))
422 a = apr_pstrcat(pool, "\"", double_quotes(pool, a), "\"", NULL);
425 *(cmdline_pos++) = ' ';
427 strcpy(cmdline_pos, a);
428 cmdline_pos += strlen(cmdline_pos);
431 *(++cmdline_pos) = 0; /* Add required second terminator */
432 cmdline_pos = strchr(cmdline, ' ');
439 /* Create environment block from list of envariables */
441 for (env_len=1, e=0; env[e]; e++)
442 env_len += strlen(env[e]) + 1;
444 env_block = apr_palloc(pool, env_len);
445 env_block_pos = env_block;
447 for (e=0; env[e]; e++) {
448 strcpy(env_block_pos, env[e]);
449 env_block_pos += strlen(env_block_pos) + 1;
452 *env_block_pos = 0; /* environment block is terminated by a double null */
456 status = DosExecPgm(error_object, sizeof(error_object),
457 attr->detached ? EXEC_BACKGROUND : EXEC_ASYNCRESULT,
458 cmdline, env_block, &rescodes, cmdline);
460 proc->pid = rescodes.codeTerminate;
462 if (attr->currdir != NULL) {
466 if (attr->child_in) {
467 apr_file_close(attr->child_in);
469 DosDupHandle(save_in, &dup);
473 if (attr->child_out) {
474 apr_file_close(attr->child_out);
476 DosDupHandle(save_out, &dup);
480 if (attr->child_err) {
481 apr_file_close(attr->child_err);
483 DosDupHandle(save_err, &dup);
490 proc->in = attr->parent_in;
491 proc->err = attr->parent_err;
492 proc->out = attr->parent_out;
498 static void proces_result_codes(RESULTCODES codes,
500 apr_exit_why_e *exitwhy)
503 apr_exit_why_e why = APR_PROC_EXIT;
505 switch (codes.codeTerminate) {
506 case TC_EXIT: /* Normal exit */
508 result = codes.codeResult;
511 case TC_HARDERROR: /* Hard error halt */
512 why = APR_PROC_SIGNAL;
516 case TC_KILLPROCESS: /* Was killed by a DosKillProcess() */
517 why = APR_PROC_SIGNAL;
521 case TC_TRAP: /* TRAP in 16 bit code */
522 case TC_EXCEPTION: /* Threw an exception (32 bit code) */
523 why = APR_PROC_SIGNAL;
525 switch (codes.codeResult | XCPT_FATAL_EXCEPTION) {
526 case XCPT_ACCESS_VIOLATION:
530 case XCPT_ILLEGAL_INSTRUCTION:
534 case XCPT_FLOAT_DIVIDE_BY_ZERO:
535 case XCPT_INTEGER_DIVIDE_BY_ZERO:
540 result = codes.codeResult;
556 APR_DECLARE(apr_status_t) apr_proc_wait_all_procs(apr_proc_t *proc,
558 apr_exit_why_e *exitwhy,
559 apr_wait_how_e waithow,
566 rc = DosWaitChild(DCWA_PROCESSTREE, waithow == APR_WAIT ? DCWW_WAIT : DCWW_NOWAIT, &codes, &pid, 0);
570 proces_result_codes(codes, exitcode, exitwhy);
571 return APR_CHILD_DONE;
572 } else if (rc == ERROR_CHILD_NOT_COMPLETE) {
573 return APR_CHILD_NOTDONE;
576 return APR_OS2_STATUS(rc);
581 APR_DECLARE(apr_status_t) apr_proc_wait(apr_proc_t *proc,
582 int *exitcode, apr_exit_why_e *exitwhy,
583 apr_wait_how_e waithow)
588 rc = DosWaitChild(DCWA_PROCESS, waithow == APR_WAIT ? DCWW_WAIT : DCWW_NOWAIT, &codes, &pid, proc->pid);
591 proces_result_codes(codes, exitcode, exitwhy);
592 return APR_CHILD_DONE;
593 } else if (rc == ERROR_CHILD_NOT_COMPLETE) {
594 return APR_CHILD_NOTDONE;
597 return APR_OS2_STATUS(rc);
602 APR_DECLARE(apr_status_t) apr_proc_detach(int daemonize)