xref: /aosp_15_r20/external/cronet/third_party/apache-portable-runtime/src/threadproc/os2/proc.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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