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 */
40 static apr_file_t no_file = { NULL, -1, };
41
apr_procattr_create(apr_procattr_t ** new,apr_pool_t * pool)42 APR_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
apr_procattr_io_set(apr_procattr_t * attr,apr_int32_t in,apr_int32_t out,apr_int32_t err)63 APR_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
apr_procattr_child_in_set(apr_procattr_t * attr,apr_file_t * child_in,apr_file_t * parent_in)113 APR_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
apr_procattr_child_out_set(apr_procattr_t * attr,apr_file_t * child_out,apr_file_t * parent_out)142 APR_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
apr_procattr_child_err_set(apr_procattr_t * attr,apr_file_t * child_err,apr_file_t * parent_err)171 APR_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
apr_procattr_dir_set(apr_procattr_t * attr,const char * dir)200 APR_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
apr_procattr_cmdtype_set(apr_procattr_t * attr,apr_cmdtype_e cmd)209 APR_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
apr_procattr_detach_set(apr_procattr_t * attr,apr_int32_t detach)216 APR_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
apr_proc_fork(apr_proc_t * proc,apr_pool_t * pool)222 APR_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 */
double_quotes(apr_pool_t * pool,const char * str)248 static 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
apr_procattr_child_errfn_set(apr_procattr_t * attr,apr_child_errfn_t * errfn)273 APR_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
apr_procattr_error_check_set(apr_procattr_t * attr,apr_int32_t chk)282 APR_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
apr_procattr_addrspace_set(apr_procattr_t * attr,apr_int32_t addrspace)289 APR_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
apr_proc_create(apr_proc_t * proc,const char * progname,const char * const * args,const char * const * env,apr_procattr_t * attr,apr_pool_t * pool)298 APR_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
proces_result_codes(RESULTCODES codes,int * exitcode,apr_exit_why_e * exitwhy)544 static 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
apr_proc_wait_all_procs(apr_proc_t * proc,int * exitcode,apr_exit_why_e * exitwhy,apr_wait_how_e waithow,apr_pool_t * p)602 APR_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
apr_proc_wait(apr_proc_t * proc,int * exitcode,apr_exit_why_e * exitwhy,apr_wait_how_e waithow)627 APR_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
apr_proc_detach(int daemonize)648 APR_DECLARE(apr_status_t) apr_proc_detach(int daemonize)
649 {
650 return APR_ENOTIMPL;
651 }
652
apr_procattr_user_set(apr_procattr_t * attr,const char * username,const char * password)653 APR_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
apr_procattr_group_set(apr_procattr_t * attr,const char * groupname)660 APR_DECLARE(apr_status_t) apr_procattr_group_set(apr_procattr_t *attr,
661 const char *groupname)
662 {
663 return APR_ENOTIMPL;
664 }
665