2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
19 #include "canaccess.h"
23 #include "../charconv/utf8.h"
24 #include "../charconv/filesys.h"
28 #include "../../pico/osdep/mswin.h"
33 /*======================================================================
36 Initiate I/O to and from a process. These functions used to be
37 similar to popen and pclose, but both an incoming stream and an
38 output file are provided.
45 * Global's to helpsignal handler tell us child's status has changed...
47 static pid_t child_pid
;
53 void zot_pipe(PIPE_S
**);
55 int pipe_mswin_exec_wrapper(char *, PIPE_S
*, unsigned,
56 void (*)(PIPE_S
*, int, void *),
59 char *pipe_error_msg(char *, char *, char *);
60 RETSIGTYPE
pipe_alarm(int);
66 /*----------------------------------------------------------------------
67 Spawn a child process and optionally connect read/write pipes to it
69 Args: command -- string to hand the shell
70 outfile -- address of pointer containing file to receive output
71 errfile -- address of pointer containing file to receive error output
72 mode -- mode for type of shell, signal protection etc...
73 Returns: pointer to alloc'd PIPE_S on success, NULL otherwise
75 The outfile is either NULL, a pointer to a NULL value, or a pointer
76 to the requested name for the output file. In the pointer-to-NULL case
77 the caller doesn't care about the name, but wants to see the pipe's
78 results so we make one up. It's up to the caller to make sure the
79 free storage containing the name is cleaned up.
81 Mode bits serve several purposes.
82 PIPE_WRITE tells us we need to open a pipe to write the child's
84 PIPE_READ tells us we need to open a pipe to read from the child's
85 stdout/stderr. *NOTE* Having neither of the above set means
86 we're not setting up any pipes, just forking the child and exec'ing
87 the command. Also, this takes precedence over any named outfile.
88 PIPE_STDERR means we're to tie the child's stderr to the same place
89 stdout is going. *NOTE* This only makes sense then if PIPE_READ
90 or an outfile is provided. Also, this takes precedence over any
92 PIPE_RESET means we reset the terminal mode to what it was before
93 we started pine and then exec the command. In PC-Pine, _RESET
94 was a shortcut for just executing a command. We'll try to pay
95 attention to the above flags to make sure we do the right thing.
96 PIPE_PROT means to protect the child from the usual nasty signals
97 that might cause premature death. Otherwise, the default signals are
98 set so the child can deal with the nasty signals in its own way.
99 NOT USED UNDER WINDOWS
100 PIPE_NOSHELL means we're to exec the command without the aid of
101 a system shell. *NOTE* This negates the affect of PIPE_USER.
102 NOT USED UNDER WINDOWS
103 PIPE_USER means we're to try executing the command in the user's
104 shell. Right now we only look in the environment, but that may get
105 more sophisticated later.
106 NOT USED UNDER WINDOWS
107 PIPE_RUNNOW was added for WINDOWS for the case pipe is called to run
108 a shell program (like for url viewing). This is the only option
109 where we don't wait for child termination, and is only obeyed if
110 PIPE_WRITE and PIPE_READ aren't set
113 open_system_pipe(char *command
, char **outfile
, char **errfile
, int mode
,
114 int timeout
, void (*pipecb_f
)(PIPE_S
*, int, void *),
115 void (*piperr_f
)(char *))
117 PIPE_S
*syspipe
= NULL
;
123 char shellpath
[MAXPATH
+1], *shell
;
124 int p
[2], oparentd
= -1, ochildd
= -1, iparentd
= -1, ichildd
= -1;
128 if(mode
& PIPE_STDERR
)
129 flags
|= MSWIN_EAW_CAPT_STDERR
;
131 * It'll be a lot more difficult to support READing and WRITing.
132 * This was never supported, and there don't look to be any cases
133 * that set both of these flags anymore for win32.
135 * errfile could probably be supported pretty easily
140 (*piperr_f
)("Pipe arg not yet supported: Error File");
146 if((mode
& PIPE_RUNNOW
)
147 && !(mode
& (PIPE_WRITE
| PIPE_READ
| PIPE_STDERR
))){
148 if(mswin_shell_exec(command
, NULL
) == 0
149 && (syspipe
= (PIPE_S
*) malloc(sizeof(PIPE_S
))) != NULL
){
150 memset(syspipe
, 0, sizeof(PIPE_S
));
157 strncpy(cmdbuf
, command
, sizeof(cmdbuf
));
158 cmdbuf
[sizeof(cmdbuf
)-1] = '\0';
160 if((syspipe
= (PIPE_S
*) malloc(sizeof(PIPE_S
))) == NULL
)
163 memset(syspipe
, 0, sizeof(PIPE_S
));
164 syspipe
->mode
= mode
;
166 syspipe
->deloutfile
= 1;
167 if(mode
& PIPE_READ
){
168 syspipe
->outfile
= temp_nam(NULL
, "po");
169 our_unlink(syspipe
->outfile
);
173 if(!*outfile
) /* asked for, but not named? */
174 *outfile
= temp_nam(NULL
, "po");
176 our_unlink(*outfile
);
177 syspipe
->outfile
= (char *) malloc((strlen(*outfile
)+1)*sizeof(char));
178 snprintf(syspipe
->outfile
, strlen(*outfile
)+1, "%s", *outfile
);
181 if(mode
& PIPE_WRITE
){
183 * Create tmp file to write, spawn child in close_pipe
184 * after tmp file's written...
186 syspipe
->infile
= temp_nam(NULL
, "pw");
187 syspipe
->out
.f
= our_fopen(syspipe
->infile
, "wb");
188 syspipe
->command
= (char *) malloc((strlen(cmdbuf
)+1)*sizeof(char));
189 snprintf(syspipe
->command
, strlen(cmdbuf
)+1, "%s", cmdbuf
);
190 dprint((1, "pipe write: %s", cmdbuf
));
192 else if(mode
& PIPE_READ
){
194 * Create a tmp file for command result, exec the command
195 * here into temp file, and return file pointer to it...
197 syspipe
->command
= (char *) malloc((strlen(cmdbuf
)+1)*sizeof(char));
198 snprintf(syspipe
->command
, strlen(cmdbuf
)+1, "%s", cmdbuf
);
199 dprint((1, "pipe read: %s", cmdbuf
));
200 if(pipe_mswin_exec_wrapper("pipe command", syspipe
,
201 flags
, pipecb_f
, piperr_f
)){
202 if(syspipe
->outfile
){
203 free((void *) syspipe
->outfile
);
204 syspipe
->outfile
= NULL
;
210 syspipe
->in
.f
= our_fopen(syspipe
->outfile
, "rb");
211 syspipe
->exit_code
= exit_code
;
215 /* we just run the command taking outfile into account */
216 syspipe
->command
= (char *) malloc((strlen(cmdbuf
)+1)*sizeof(char));
217 snprintf(syspipe
->command
, strlen(cmdbuf
)+1, "%s", cmdbuf
);
218 if(pipe_mswin_exec_wrapper("pipe command", syspipe
,
219 flags
, pipecb_f
, piperr_f
)){
220 if(syspipe
->outfile
){
221 free((void *) syspipe
->outfile
);
222 syspipe
->outfile
= NULL
;
228 syspipe
->exit_code
= exit_code
;
231 #else /* !_WINDOWS */
233 if((syspipe
= (PIPE_S
*) malloc(sizeof(PIPE_S
))) == NULL
)
236 memset(syspipe
, 0, sizeof(PIPE_S
));
238 syspipe
->mode
= mode
;
241 * If we're not using the shell's command parsing smarts, build
244 if(mode
& PIPE_NOSHELL
){
248 /* parse the arguments into argv */
249 for(p
= command
; *p
&& isspace((unsigned char)(*p
)); p
++)
250 ; /* swallow leading ws */
255 if((syspipe
->args
= (char *) malloc((l
+ 1) * sizeof(char))) != NULL
){
256 strncpy(syspipe
->args
, p
, l
);
257 syspipe
->args
[l
] = '\0';
261 (*piperr_f
)(pipe_error_msg("<null>", "execute",
262 "Can't allocate command string"));
269 (*piperr_f
)(pipe_error_msg("<null>", "execute",
270 "No command name found"));
275 for(p
= syspipe
->args
, n
= 2; *p
; p
++) /* count the args */
276 if(isspace((unsigned char)(*p
))
277 && *(p
+1) && !isspace((unsigned char)(*(p
+1))))
280 if ((syspipe
->argv
= ap
= (char **)malloc(n
* sizeof(char *))) == NULL
){
285 memset(syspipe
->argv
, 0, n
* sizeof(char *));
287 for(p
= syspipe
->args
; *p
; ){ /* collect args */
288 while(*p
&& isspace((unsigned char)(*p
)))
291 *ap
++ = (*p
) ? p
: NULL
;
292 while(*p
&& !isspace((unsigned char)(*p
)))
296 /* make sure argv[0] exists in $PATH */
297 if(can_access_in_path(getenv("PATH"), syspipe
->argv
[0],
298 EXECUTE_ACCESS
) < 0){
300 (*piperr_f
)(pipe_error_msg(syspipe
->argv
[0], "access",
301 error_description(errno
)));
307 /* fill in any output filenames */
308 if(!(mode
& PIPE_READ
)){
309 if(outfile
&& !*outfile
)
310 *outfile
= temp_nam(NULL
, "pine_p"); /* asked for, but not named? */
312 if(errfile
&& !*errfile
)
313 *errfile
= temp_nam(NULL
, "pine_p"); /* ditto */
317 if(mode
& (PIPE_WRITE
| PIPE_READ
)){
318 if(mode
& PIPE_WRITE
){
319 pipe(p
); /* alloc pipe to write child */
320 oparentd
= p
[STDOUT_FILENO
];
321 ichildd
= p
[STDIN_FILENO
];
324 if(mode
& PIPE_READ
){
325 pipe(p
); /* alloc pipe to read child */
326 iparentd
= p
[STDIN_FILENO
];
327 ochildd
= p
[STDOUT_FILENO
];
331 if(pipecb_f
) /* let caller prep display */
332 (*pipecb_f
)(syspipe
, OSB_PRE_OPEN
, NULL
);
335 if((syspipe
->pid
= vfork()) == 0){
336 /* reset child's handlers in requested fashion... */
337 (void)signal(SIGINT
, (mode
& PIPE_PROT
) ? SIG_IGN
: SIG_DFL
);
338 (void)signal(SIGQUIT
, (mode
& PIPE_PROT
) ? SIG_IGN
: SIG_DFL
);
339 (void)signal(SIGHUP
, (mode
& PIPE_PROT
) ? SIG_IGN
: SIG_DFL
);
341 (void) signal(SIGCHLD
, SIG_DFL
);
344 /* if parent isn't reading, and we have a filename to write */
345 if(!(mode
& PIPE_READ
) && outfile
){ /* connect output to file */
346 int output
= our_creat(*outfile
, 0600);
347 dup2(output
, STDOUT_FILENO
);
348 if(mode
& PIPE_STDERR
)
349 dup2(output
, STDERR_FILENO
);
351 dup2(our_creat(*errfile
, 0600), STDERR_FILENO
);
354 if(mode
& PIPE_WRITE
){ /* connect process input */
356 dup2(ichildd
, STDIN_FILENO
); /* tie stdin to pipe */
360 if(mode
& PIPE_READ
){ /* connect process output */
362 dup2(ochildd
, STDOUT_FILENO
); /* tie std{out,err} to pipe */
363 if(mode
& PIPE_STDERR
)
364 dup2(ochildd
, STDERR_FILENO
);
366 dup2(our_creat(*errfile
, 0600), STDERR_FILENO
);
371 if(mode
& PIPE_NOSHELL
){
372 execvp(syspipe
->argv
[0], syspipe
->argv
);
375 if(mode
& PIPE_USER
){
377 if((env
= getenv("SHELL")) && (sh
= strrchr(env
, '/'))){
379 strncpy(shellpath
, env
, sizeof(shellpath
)-1);
380 shellpath
[sizeof(shellpath
)-1] = '\0';
384 strncpy(shellpath
, "/bin/csh", sizeof(shellpath
)-1);
385 shellpath
[sizeof(shellpath
)-1] = '\0';
390 strncpy(shellpath
, "/bin/sh", sizeof(shellpath
)-1);
391 shellpath
[sizeof(shellpath
)-1] = '\0';
394 execl(shellpath
, shell
, command
? "-c" : (char *)NULL
, fname_to_locale(command
), (char *)NULL
);
397 fprintf(stderr
, "Can't exec %s\nReason: %s",
398 command
, error_description(errno
));
402 if((child_pid
= syspipe
->pid
) > 0){
403 syspipe
->isig
= signal(SIGINT
, SIG_IGN
); /* Reset handlers to make */
404 syspipe
->qsig
= signal(SIGQUIT
, SIG_IGN
); /* sure we don't come to */
405 syspipe
->hsig
= signal(SIGHUP
, SIG_IGN
); /* a premature end... */
406 if((syspipe
->timeout
= timeout
) != 0){
407 syspipe
->alrm
= signal(SIGALRM
, pipe_alarm
);
408 syspipe
->old_timeo
= alarm(timeout
);
411 if(mode
& PIPE_WRITE
){
414 syspipe
->out
.d
= oparentd
;
416 syspipe
->out
.f
= fdopen(oparentd
, "w");
419 if(mode
& PIPE_READ
){
422 syspipe
->in
.d
= iparentd
;
424 syspipe
->in
.f
= fdopen(iparentd
, "r");
428 if(mode
& (PIPE_WRITE
| PIPE_READ
)){
429 if(mode
& PIPE_WRITE
){
434 if(mode
& PIPE_READ
){
440 if(pipecb_f
) /* let caller fixup display */
441 (*pipecb_f
)(syspipe
, OSB_POST_OPEN
, NULL
);
443 if(outfile
&& *outfile
){
444 our_unlink(*outfile
);
445 free((void *) *outfile
);
449 if(errfile
&& *errfile
){
450 our_unlink(*errfile
);
451 free((void *) *errfile
);
456 (*piperr_f
)(pipe_error_msg(command
, "fork",
457 error_description(errno
)));
469 /*----------------------------------------------------------------------
470 Return appropriate error message
472 Args: cmd -- command we were trying to exec
473 op -- operation leading up to the exec
474 res -- result of that operation
478 pipe_error_msg(char *cmd
, char *op
, char *res
)
480 static char ebuf
[512 + 16 + 1];
482 snprintf(ebuf
, sizeof(ebuf
), "Pipe can't %.256s \"%.32sb\": %.223s",
483 op
? op
: "?", cmd
? cmd
: "?", res
? res
: "?");
484 ebuf
[sizeof(ebuf
) - 1] = '\0';
488 #endif /* !_WINDOWS */
491 /*----------------------------------------------------------------------
492 Free resources associated with the given pipe struct
494 Args: syspipe -- address of pointer to struct to clean up
498 zot_pipe(PIPE_S
**syspipe
)
500 if((*syspipe
)->args
){
501 free((void *) (*syspipe
)->args
);
502 (*syspipe
)->args
= NULL
;
505 if((*syspipe
)->argv
){
506 free((void *) (*syspipe
)->argv
);
507 (*syspipe
)->argv
= NULL
;
511 free((void *) (*syspipe
)->tmp
);
512 (*syspipe
)->tmp
= NULL
;
517 if((*syspipe
)->outfile
){
518 free((void *) (*syspipe
)->outfile
);
519 (*syspipe
)->outfile
= NULL
;
522 if((*syspipe
)->command
){
523 free((void *) (*syspipe
)->command
);
524 (*syspipe
)->command
= NULL
;
527 #endif /* _WINDOWS */
529 free((void *) *syspipe
);
536 * Returns: 0 if all went well, -1 otherwise
539 pipe_close_write(PIPE_S
*syspipe
)
543 if(!syspipe
|| !syspipe
->out
.f
)
551 if(syspipe
->mode
& PIPE_STDERR
)
552 flags
|= MSWIN_EAW_CAPT_STDERR
;
554 rv
= fclose(syspipe
->out
.f
);
555 syspipe
->out
.f
= NULL
;
556 if(syspipe
->mode
& PIPE_WRITE
){
558 * PIPE_WRITE should always be set if we're trying to close
560 * PIPE_WRITE can't start process till now, all the others
561 * will have already run
563 if(pipe_mswin_exec_wrapper("pipe command", syspipe
,
565 /* some horrible error just occurred */
568 syspipe
->in
.f
= our_fopen(syspipe
->outfile
, "rb");
576 rv
= fclose(syspipe
->out
.f
) ? -1 : 0;
577 syspipe
->out
.f
= NULL
;
585 /*----------------------------------------------------------------------
586 Close pipe previously allocated and wait for child's death
588 Args: syspipe -- address of pointer to struct returned by open_system_pipe
589 exitval -- return exit status here.
592 Two modes of return values for backcompat.
594 returns exit status of child or -1 if invalid syspipe
596 returns -1 if invalid syspipe or 0 if ok. In that case, exitval
597 of child is returned in exitval
600 close_system_pipe(PIPE_S
**syspipe
, int *exitval
, void (*pipecb_f
) (PIPE_S
*, int, void *))
606 if(!(syspipe
&& *syspipe
))
609 if((*syspipe
)->mode
& PIPE_STDERR
)
610 flags
|= MSWIN_EAW_CAPT_STDERR
;
612 if(((*syspipe
)->mode
& PIPE_WRITE
) && (*syspipe
)->out
.f
){
613 fclose((*syspipe
)->out
.f
);
615 * PIPE_WRITE can't start process till now, all the others
616 * will have already run
618 if(pipe_mswin_exec_wrapper("pipe command", (*syspipe
),
619 flags
, pipecb_f
, NULL
))
620 /* some horrible error just occurred */
623 else if((*syspipe
)->mode
& PIPE_READ
)
625 fclose((*syspipe
)->in
.f
);
628 *exitval
= (*syspipe
)->exit_code
;
629 dprint((5, "Closed pipe: exitval=%d\n", *exitval
));
632 if((*syspipe
)->infile
)
633 our_unlink((*syspipe
)->infile
);
635 if((*syspipe
)->outfile
&& (*syspipe
)->deloutfile
)
636 our_unlink((*syspipe
)->outfile
);
638 if(rv
!= -1 && !exitval
)
639 rv
= (*syspipe
)->exit_code
;
645 dprint((5, "Closed pipe: rv=%d\n", rv
));
654 if(!(syspipe
&& *syspipe
))
657 if(((*syspipe
)->mode
) & PIPE_WRITE
){
658 if(((*syspipe
)->mode
) & PIPE_DESC
){
659 if((*syspipe
)->out
.d
>= 0)
660 close((*syspipe
)->out
.d
);
662 else if((*syspipe
)->out
.f
)
663 fclose((*syspipe
)->out
.f
);
666 if(((*syspipe
)->mode
) & PIPE_READ
){
667 if(((*syspipe
)->mode
) & PIPE_DESC
){
668 if((*syspipe
)->in
.d
>= 0)
669 close((*syspipe
)->in
.d
);
671 else if((*syspipe
)->in
.f
)
672 fclose((*syspipe
)->in
.f
);
676 (*pipecb_f
)(*syspipe
, OSB_PRE_CLOSE
, NULL
);
678 /* wait on the child */
679 (void) process_reap((*syspipe
)->pid
, &status
, PR_NONE
);
681 /* restore original handlers... */
682 (void) signal(SIGINT
, (*syspipe
)->isig
);
683 (void) signal(SIGHUP
, (*syspipe
)->hsig
);
684 (void) signal(SIGQUIT
, (*syspipe
)->qsig
);
686 if((*syspipe
)->timeout
){
687 (void)signal(SIGALRM
, (*syspipe
)->alrm
);
688 alarm((*syspipe
)->old_timeo
);
693 (*pipecb_f
)(*syspipe
, OSB_POST_CLOSE
, NULL
);
709 * process_reap - manage child demise and return exit status
711 * Args: pid -- id of process to reap
712 * esp -- pointer to exist status
713 * flags -- special reaping considerations
716 * < 0 -- if there's a problem
717 * 0 -- if no child to reap
718 * > 0 -- process id of the child
721 process_reap(pid_t pid
, int *esp
, int flags
)
728 WAITSTATUS_T wstatus
;
737 if(flags
& PR_NOHANG
)
741 while (((rv
= waitpid(pid
, &wstatus
, wflags
)) < 0) && (errno
!= ECHILD
));
748 if(flags
& PR_NOHANG
)
752 while (((rv
= wait4(pid
,&wstatus
,wflags
,NULL
)) < 0) && (errno
!= ECHILD
));
756 while (((rv
= wait(&wstatus
)) != pid
) && ((rv
> 0) || (errno
!= ECHILD
)));
765 *esp
= (WIFEXITED(wstatus
)) ? (int) WEXITSTATUS(wstatus
) : -1;
777 kill(child_pid
, SIGINT
);
779 #endif /* !_WINDOWS */
784 * Wrapper around mswin_exec_and_wait()
787 pipe_mswin_exec_wrapper(char *whatsit
,
788 PIPE_S
*syspipe
, unsigned flags
,
789 void (*pipecb_f
)(PIPE_S
*, int, void *),
790 void (*piperr_f
)(char *))
794 flags
|= MSWIN_EAW_CTRL_C_CANCELS
;
797 (*pipecb_f
)(syspipe
, OSB_PRE_OPEN
, NULL
);
799 rv
= mswin_exec_and_wait(whatsit
, syspipe
->command
,
800 syspipe
->infile
, syspipe
->outfile
,
801 &syspipe
->exit_code
, flags
);
804 (*pipecb_f
)(syspipe
, OSB_POST_OPEN
, (void *)rv
);