diff options
author | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
---|---|---|
committer | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
commit | 094ca96844842928810f14844413109fc6cdd890 (patch) | |
tree | e60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pith/osdep/pipe.c | |
download | alpine-094ca96844842928810f14844413109fc6cdd890.tar.xz |
Initial Alpine Version
Diffstat (limited to 'pith/osdep/pipe.c')
-rw-r--r-- | pith/osdep/pipe.c | 811 |
1 files changed, 811 insertions, 0 deletions
diff --git a/pith/osdep/pipe.c b/pith/osdep/pipe.c new file mode 100644 index 00000000..2183f529 --- /dev/null +++ b/pith/osdep/pipe.c @@ -0,0 +1,811 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: pipe.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2008 University of Washington + * Copyright 2013 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * ======================================================================== + */ + +#include <system.h> + +#include "err_desc.h" + +#include "canaccess.h" +#include "temp_nam.h" +#include "forkwait.h" +#include "pipe.h" +#include "../charconv/utf8.h" +#include "../charconv/filesys.h" +#include "../debug.h" + +#ifdef _WINDOWS +#include "../../pico/osdep/mswin.h" +#endif + + + +/*====================================================================== + pipe + + Initiate I/O to and from a process. These functions used to be + similar to popen and pclose, but both an incoming stream and an + output file are provided. + + ====*/ + + + +/* + * Global's to helpsignal handler tell us child's status has changed... + */ +static pid_t child_pid; + + +/* + * Internal Protos + */ +void zot_pipe(PIPE_S **); +#ifdef _WINDOWS +int pipe_mswin_exec_wrapper(char *, PIPE_S *, unsigned, + void (*)(PIPE_S *, int, void *), + void (*)(char *)); +#else /* UNIX */ +char *pipe_error_msg(char *, char *, char *); +RETSIGTYPE pipe_alarm(int); +#endif /* UNIX */ + + + + +/*---------------------------------------------------------------------- + Spawn a child process and optionally connect read/write pipes to it + + Args: command -- string to hand the shell + outfile -- address of pointer containing file to receive output + errfile -- address of pointer containing file to receive error output + mode -- mode for type of shell, signal protection etc... + Returns: pointer to alloc'd PIPE_S on success, NULL otherwise + + The outfile is either NULL, a pointer to a NULL value, or a pointer + to the requested name for the output file. In the pointer-to-NULL case + the caller doesn't care about the name, but wants to see the pipe's + results so we make one up. It's up to the caller to make sure the + free storage containing the name is cleaned up. + + Mode bits serve several purposes. + PIPE_WRITE tells us we need to open a pipe to write the child's + stdin. + PIPE_READ tells us we need to open a pipe to read from the child's + stdout/stderr. *NOTE* Having neither of the above set means + we're not setting up any pipes, just forking the child and exec'ing + the command. Also, this takes precedence over any named outfile. + PIPE_STDERR means we're to tie the childs stderr to the same place + stdout is going. *NOTE* This only makes sense then if PIPE_READ + or an outfile is provided. Also, this takes precedence over any + named errfile. + PIPE_RESET means we reset the terminal mode to what it was before + we started pine and then exec the command. In PC-Pine, _RESET + was a shortcut for just executing a command. We'll try to pay + attention to the above flags to make sure we do the right thing. + PIPE_PROT means to protect the child from the usual nasty signals + that might cause premature death. Otherwise, the default signals are + set so the child can deal with the nasty signals in its own way. + NOT USED UNDER WINDOWS + PIPE_NOSHELL means we're to exec the command without the aid of + a system shell. *NOTE* This negates the affect of PIPE_USER. + NOT USED UNDER WINDOWS + PIPE_USER means we're to try executing the command in the user's + shell. Right now we only look in the environment, but that may get + more sophisticated later. + NOT USED UNDER WINDOWS + PIPE_RUNNOW was added for WINDOWS for the case pipe is called to run + a shell program (like for url viewing). This is the only option + where we don't wait for child termination, and is only obeyed if + PIPE_WRITE and PIPE_READ aren't set + ----*/ +PIPE_S * +open_system_pipe(char *command, char **outfile, char **errfile, int mode, + int timeout, void (*pipecb_f)(PIPE_S *, int, void *), + void (*piperr_f)(char *)) +{ + PIPE_S *syspipe = NULL; +#ifdef _WINDOWS + int exit_code = 0; + char cmdbuf[1024]; + unsigned flags = 0; +#else + char shellpath[MAXPATH+1], *shell; + int p[2], oparentd = -1, ochildd = -1, iparentd = -1, ichildd = -1; +#endif + +#ifdef _WINDOWS + if(mode & PIPE_STDERR) + flags |= MSWIN_EAW_CAPT_STDERR; + /* + * It'll be a lot more difficult to support READing and WRITing. + * This was never supported, and there don't look to be any cases + * that set both of these flags anymore for win32. + * + * errfile could probably be supported pretty easily + */ + + if(errfile){ + if(piperr_f) + (*piperr_f)("Pipe arg not yet supported: Error File"); + + return(NULL); + } + + + if((mode & PIPE_RUNNOW) + && !(mode & (PIPE_WRITE | PIPE_READ | PIPE_STDERR))){ + if(mswin_shell_exec(command, NULL) == 0 + && (syspipe = (PIPE_S *) malloc(sizeof(PIPE_S))) != NULL){ + memset(syspipe, 0, sizeof(PIPE_S)); + return(syspipe); + } + + return(NULL); + } + + strncpy(cmdbuf, command, sizeof(cmdbuf)); + cmdbuf[sizeof(cmdbuf)-1] = '\0'; + + if((syspipe = (PIPE_S *) malloc(sizeof(PIPE_S))) == NULL) + return(NULL); + + memset(syspipe, 0, sizeof(PIPE_S)); + syspipe->mode = mode; + if(!outfile){ + syspipe->deloutfile = 1; + if(mode & PIPE_READ){ + syspipe->outfile = temp_nam(NULL, "po"); + our_unlink(syspipe->outfile); + } + } + else{ + if(!*outfile) /* asked for, but not named? */ + *outfile = temp_nam(NULL, "po"); + + our_unlink(*outfile); + syspipe->outfile = (char *) malloc((strlen(*outfile)+1)*sizeof(char)); + snprintf(syspipe->outfile, strlen(*outfile)+1, "%s", *outfile); + } + + if(mode & PIPE_WRITE){ + /* + * Create tmp file to write, spawn child in close_pipe + * after tmp file's written... + */ + syspipe->infile = temp_nam(NULL, "pw"); + syspipe->out.f = our_fopen(syspipe->infile, "wb"); + syspipe->command = (char *) malloc((strlen(cmdbuf)+1)*sizeof(char)); + snprintf(syspipe->command, strlen(cmdbuf)+1, "%s", cmdbuf); + dprint((1, "pipe write: %s", cmdbuf)); + } + else if(mode & PIPE_READ){ + /* + * Create a tmp file for command result, exec the command + * here into temp file, and return file pointer to it... + */ + syspipe->command = (char *) malloc((strlen(cmdbuf)+1)*sizeof(char)); + snprintf(syspipe->command, strlen(cmdbuf)+1, "%s", cmdbuf); + dprint((1, "pipe read: %s", cmdbuf)); + if(pipe_mswin_exec_wrapper("pipe command", syspipe, + flags, pipecb_f, piperr_f)){ + if(syspipe->outfile){ + free((void *) syspipe->outfile); + syspipe->outfile = NULL; + } + + zot_pipe(&syspipe); + } + else{ + syspipe->in.f = our_fopen(syspipe->outfile, "rb"); + syspipe->exit_code = exit_code; + } + } + else{ + /* we just run the command taking outfile into account */ + syspipe->command = (char *) malloc((strlen(cmdbuf)+1)*sizeof(char)); + snprintf(syspipe->command, strlen(cmdbuf)+1, "%s", cmdbuf); + if(pipe_mswin_exec_wrapper("pipe command", syspipe, + flags, pipecb_f, piperr_f)){ + if(syspipe->outfile){ + free((void *) syspipe->outfile); + syspipe->outfile = NULL; + } + + zot_pipe(&syspipe); + } + else + syspipe->exit_code = exit_code; + } + +#else /* !_WINDOWS */ + + if((syspipe = (PIPE_S *) malloc(sizeof(PIPE_S))) == NULL) + return(NULL); + + memset(syspipe, 0, sizeof(PIPE_S)); + + syspipe->mode = mode; + + /* + * If we're not using the shell's command parsing smarts, build + * argv by hand... + */ + if(mode & PIPE_NOSHELL){ + char **ap, *p; + size_t n; + + /* parse the arguments into argv */ + for(p = command; *p && isspace((unsigned char)(*p)); p++) + ; /* swallow leading ws */ + + if(*p){ + int l = strlen(p); + + if((syspipe->args = (char *) malloc((l + 1) * sizeof(char))) != NULL){ + strncpy(syspipe->args, p, l); + syspipe->args[l] = '\0'; + } + else{ + if(piperr_f) + (*piperr_f)(pipe_error_msg("<null>", "execute", + "Can't allocate command string")); + zot_pipe(&syspipe); + return(NULL); + } + } + else{ + if(piperr_f) + (*piperr_f)(pipe_error_msg("<null>", "execute", + "No command name found")); + zot_pipe(&syspipe); + return(NULL); + } + + for(p = syspipe->args, n = 2; *p; p++) /* count the args */ + if(isspace((unsigned char)(*p)) + && *(p+1) && !isspace((unsigned char)(*(p+1)))) + n++; + + if ((syspipe->argv = ap = (char **)malloc(n * sizeof(char *))) == NULL){ + zot_pipe(&syspipe); + return(NULL); + } + + memset(syspipe->argv, 0, n * sizeof(char *)); + + for(p = syspipe->args; *p; ){ /* collect args */ + while(*p && isspace((unsigned char)(*p))) + *p++ = '\0'; + + *ap++ = (*p) ? p : NULL; + while(*p && !isspace((unsigned char)(*p))) + p++; + } + + /* make sure argv[0] exists in $PATH */ + if(can_access_in_path(getenv("PATH"), syspipe->argv[0], + EXECUTE_ACCESS) < 0){ + if(piperr_f) + (*piperr_f)(pipe_error_msg(syspipe->argv[0], "access", + error_description(errno))); + zot_pipe(&syspipe); + return(NULL); + } + } + + /* fill in any output filenames */ + if(!(mode & PIPE_READ)){ + if(outfile && !*outfile) + *outfile = temp_nam(NULL, "pine_p"); /* asked for, but not named? */ + + if(errfile && !*errfile) + *errfile = temp_nam(NULL, "pine_p"); /* ditto */ + } + + /* create pipes */ + if(mode & (PIPE_WRITE | PIPE_READ)){ + if(mode & PIPE_WRITE){ + pipe(p); /* alloc pipe to write child */ + oparentd = p[STDOUT_FILENO]; + ichildd = p[STDIN_FILENO]; + } + + if(mode & PIPE_READ){ + pipe(p); /* alloc pipe to read child */ + iparentd = p[STDIN_FILENO]; + ochildd = p[STDOUT_FILENO]; + } + } + + if(pipecb_f) /* let caller prep display */ + (*pipecb_f)(syspipe, OSB_PRE_OPEN, NULL); + + + if((syspipe->pid = vfork()) == 0){ + /* reset child's handlers in requested fashion... */ + (void)signal(SIGINT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL); + (void)signal(SIGQUIT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL); + (void)signal(SIGHUP, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL); +#ifdef SIGCHLD + (void) signal(SIGCHLD, SIG_DFL); +#endif + + /* if parent isn't reading, and we have a filename to write */ + if(!(mode & PIPE_READ) && outfile){ /* connect output to file */ + int output = our_creat(*outfile, 0600); + dup2(output, STDOUT_FILENO); + if(mode & PIPE_STDERR) + dup2(output, STDERR_FILENO); + else if(errfile) + dup2(our_creat(*errfile, 0600), STDERR_FILENO); + } + + if(mode & PIPE_WRITE){ /* connect process input */ + close(oparentd); + dup2(ichildd, STDIN_FILENO); /* tie stdin to pipe */ + close(ichildd); + } + + if(mode & PIPE_READ){ /* connect process output */ + close(iparentd); + dup2(ochildd, STDOUT_FILENO); /* tie std{out,err} to pipe */ + if(mode & PIPE_STDERR) + dup2(ochildd, STDERR_FILENO); + else if(errfile) + dup2(our_creat(*errfile, 0600), STDERR_FILENO); + + close(ochildd); + } + + if(mode & PIPE_NOSHELL){ + execvp(syspipe->argv[0], syspipe->argv); + } + else{ + if(mode & PIPE_USER){ + char *env, *sh; + if((env = getenv("SHELL")) && (sh = strrchr(env, '/'))){ + shell = sh + 1; + strncpy(shellpath, env, sizeof(shellpath)-1); + shellpath[sizeof(shellpath)-1] = '\0'; + } + else{ + shell = "csh"; + strncpy(shellpath, "/bin/csh", sizeof(shellpath)-1); + shellpath[sizeof(shellpath)-1] = '\0'; + } + } + else{ + shell = "sh"; + strncpy(shellpath, "/bin/sh", sizeof(shellpath)-1); + shellpath[sizeof(shellpath)-1] = '\0'; + } + + execl(shellpath, shell, command ? "-c" : (char *)NULL, fname_to_locale(command), (char *)NULL); + } + + fprintf(stderr, "Can't exec %s\nReason: %s", + command, error_description(errno)); + _exit(-1); + } + + if((child_pid = syspipe->pid) > 0){ + syspipe->isig = signal(SIGINT, SIG_IGN); /* Reset handlers to make */ + syspipe->qsig = signal(SIGQUIT, SIG_IGN); /* sure we don't come to */ + syspipe->hsig = signal(SIGHUP, SIG_IGN); /* a premature end... */ + if((syspipe->timeout = timeout) != 0){ + syspipe->alrm = signal(SIGALRM, pipe_alarm); + syspipe->old_timeo = alarm(timeout); + } + + if(mode & PIPE_WRITE){ + close(ichildd); + if(mode & PIPE_DESC) + syspipe->out.d = oparentd; + else + syspipe->out.f = fdopen(oparentd, "w"); + } + + if(mode & PIPE_READ){ + close(ochildd); + if(mode & PIPE_DESC) + syspipe->in.d = iparentd; + else + syspipe->in.f = fdopen(iparentd, "r"); + } + } + else{ + if(mode & (PIPE_WRITE | PIPE_READ)){ + if(mode & PIPE_WRITE){ + close(oparentd); + close(ichildd); + } + + if(mode & PIPE_READ){ + close(iparentd); + close(ochildd); + } + } + + if(pipecb_f) /* let caller fixup display */ + (*pipecb_f)(syspipe, OSB_POST_OPEN, NULL); + + if(outfile && *outfile){ + our_unlink(*outfile); + free((void *) *outfile); + *outfile = NULL; + } + + if(errfile && *errfile){ + our_unlink(*errfile); + free((void *) *errfile); + *errfile = NULL; + } + + if(piperr_f) + (*piperr_f)(pipe_error_msg(command, "fork", + error_description(errno))); + zot_pipe(&syspipe); + } + +#endif /* UNIX */ + + return(syspipe); +} + + + +#ifndef _WINDOWS +/*---------------------------------------------------------------------- + Return appropriate error message + + Args: cmd -- command we were trying to exec + op -- operation leading up to the exec + res -- result of that operation + + ----*/ +char * +pipe_error_msg(char *cmd, char *op, char *res) +{ + static char ebuf[512]; + + snprintf(ebuf, 256, "Pipe can't %.256s \"%.32sb\": %.223s", + op ? op : "?", cmd ? cmd : "?", res ? res : "?"); + + return(ebuf); +} +#endif /* !_WINDOWS */ + + +/*---------------------------------------------------------------------- + Free resources associated with the given pipe struct + + Args: syspipe -- address of pointer to struct to clean up + + ----*/ +void +zot_pipe(PIPE_S **syspipe) +{ + if((*syspipe)->args){ + free((void *) (*syspipe)->args); + (*syspipe)->args = NULL; + } + + if((*syspipe)->argv){ + free((void *) (*syspipe)->argv); + (*syspipe)->argv = NULL; + } + + if((*syspipe)->tmp){ + free((void *) (*syspipe)->tmp); + (*syspipe)->tmp = NULL; + } + +#ifdef _WINDOWS + + if((*syspipe)->outfile){ + free((void *) (*syspipe)->outfile); + (*syspipe)->outfile = NULL; + } + + if((*syspipe)->command){ + free((void *) (*syspipe)->command); + (*syspipe)->command = NULL; + } + +#endif /* _WINDOWS */ + + free((void *) *syspipe); + *syspipe = NULL; +} + + + +/* + * Returns: 0 if all went well, -1 otherwise + */ +int +pipe_close_write(PIPE_S *syspipe) +{ + int rv = 0; + + if(!syspipe || !syspipe->out.f) + return -1; + +#ifdef _WINDOWS + + { + unsigned flags = 0; + + if(syspipe->mode & PIPE_STDERR) + flags |= MSWIN_EAW_CAPT_STDERR; + + rv = fclose(syspipe->out.f); + syspipe->out.f = NULL; + if(syspipe->mode & PIPE_WRITE){ + /* + * PIPE_WRITE should always be set if we're trying to close + * the write end. + * PIPE_WRITE can't start process till now, all the others + * will have already run + */ + if(pipe_mswin_exec_wrapper("pipe command", syspipe, + flags, NULL, NULL)) + /* some horrible error just occurred */ + rv = -1; + else + syspipe->in.f = our_fopen(syspipe->outfile, "rb"); + } + else + rv = -1; + } + +#else /* UNIX */ + + rv = fclose(syspipe->out.f) ? -1 : 0; + syspipe->out.f = NULL; + +#endif + return(rv); +} + + + +/*---------------------------------------------------------------------- + Close pipe previously allocated and wait for child's death + + Args: syspipe -- address of pointer to struct returned by open_system_pipe + exitval -- return exit status here. + + Returns: + Two modes of return values for backcompat. + If exitval == NULL + returns exit status of child or -1 if invalid syspipe + If exitval != NULL + returns -1 if invalid syspipe or 0 if ok. In that case, exitval + of child is returned in exitval + ----*/ +int +close_system_pipe(PIPE_S **syspipe, int *exitval, void (*pipecb_f) (PIPE_S *, int, void *)) +{ +#ifdef _WINDOWS + int rv = 0; + unsigned flags = 0; + + if(!(syspipe && *syspipe)) + return(-1); + + if((*syspipe)->mode & PIPE_STDERR) + flags |= MSWIN_EAW_CAPT_STDERR; + + if(((*syspipe)->mode & PIPE_WRITE) && (*syspipe)->out.f){ + fclose((*syspipe)->out.f); + /* + * PIPE_WRITE can't start process till now, all the others + * will have already run + */ + if(pipe_mswin_exec_wrapper("pipe command", (*syspipe), + flags, pipecb_f, NULL)) + /* some horrible error just occurred */ + rv = -1; + } + else if((*syspipe)->mode & PIPE_READ) + if((*syspipe)->in.f) + fclose((*syspipe)->in.f); + + if(exitval){ + *exitval = (*syspipe)->exit_code; + dprint((5, "Closed pipe: exitval=%d\n", *exitval)); + } + + if((*syspipe)->infile) + our_unlink((*syspipe)->infile); + + if((*syspipe)->outfile && (*syspipe)->deloutfile) + our_unlink((*syspipe)->outfile); + + if(rv != -1 && !exitval) + rv = (*syspipe)->exit_code; + + zot_pipe(syspipe); + +#ifdef DEBUG + if(!exitval){ + dprint((5, "Closed pipe: rv=%d\n", rv)); + } +#endif /* DEBUG */ + + return(rv); + +#else /* UNIX */ + int status; + + if(!(syspipe && *syspipe)) + return -1; + + if(((*syspipe)->mode) & PIPE_WRITE){ + if(((*syspipe)->mode) & PIPE_DESC){ + if((*syspipe)->out.d >= 0) + close((*syspipe)->out.d); + } + else if((*syspipe)->out.f) + fclose((*syspipe)->out.f); + } + + if(((*syspipe)->mode) & PIPE_READ){ + if(((*syspipe)->mode) & PIPE_DESC){ + if((*syspipe)->in.d >= 0) + close((*syspipe)->in.d); + } + else if((*syspipe)->in.f) + fclose((*syspipe)->in.f); + } + + if(pipecb_f) + (*pipecb_f)(*syspipe, OSB_PRE_CLOSE, NULL); + + /* wait on the child */ + (void) process_reap((*syspipe)->pid, &status, PR_NONE); + + /* restore original handlers... */ + (void) signal(SIGINT, (*syspipe)->isig); + (void) signal(SIGHUP, (*syspipe)->hsig); + (void) signal(SIGQUIT, (*syspipe)->qsig); + + if((*syspipe)->timeout){ + (void)signal(SIGALRM, (*syspipe)->alrm); + alarm((*syspipe)->old_timeo); + child_pid = 0; + } + + if(pipecb_f) + (*pipecb_f)(*syspipe, OSB_POST_CLOSE, NULL); + + zot_pipe(syspipe); + + if(exitval){ + *exitval = status; + return 0; + } + else{ + return(status); + } +#endif /* UNIX */ +} + + +/* + * process_reap - manage child demise and return exit status + * + * Args: pid -- id of process to reap + * esp -- pointer to exist status + * flags -- special reaping considerations + * + * Returns: + * < 0 -- if there's a problem + * 0 -- if no child to reap + * > 0 -- process id of the child + */ +pid_t +process_reap(pid_t pid, int *esp, int flags) +{ +#ifdef _WINDOWS + + return 0; + +#else /* UNIX */ + WAITSTATUS_T wstatus; + pid_t rv; + int wflags; + +#if HAVE_WAITPID + + wflags = 0; + +#ifdef WNOHANG + if(flags & PR_NOHANG) + wflags |= WNOHANG; +#endif + + while (((rv = waitpid(pid, &wstatus, wflags)) < 0) && (errno != ECHILD)); + +#elif HAVE_WAIT4 + + wflags = 0; + +#ifdef WNOHANG + if(flags & PR_NOHANG) + wflags |= WNOHANG; +#endif + + while (((rv = wait4(pid,&wstatus,wflags,NULL)) < 0) && (errno != ECHILD)); + +#elif HAVE_WAIT + + while (((rv = wait(&wstatus)) != pid) && ((rv > 0) || (errno != ECHILD))); + +#else + + /* BUG: BAIL */ + +#endif + + if(rv > 0) + *esp = (WIFEXITED(wstatus)) ? (int) WEXITSTATUS(wstatus) : -1; + + return(rv); +#endif /* UNIX */ +} + + +#ifndef _WINDOWS +RETSIGTYPE +pipe_alarm(int sig) +{ + if(child_pid) + kill(child_pid, SIGINT); +} +#endif /* !_WINDOWS */ + + +#ifdef _WINDOWS +/* + * Wrapper around mswin_exec_and_wait() + */ +int +pipe_mswin_exec_wrapper(char *whatsit, + PIPE_S *syspipe, unsigned flags, + void (*pipecb_f)(PIPE_S *, int, void *), + void (*piperr_f)(char *)) +{ + int rv; + + flags |= MSWIN_EAW_CTRL_C_CANCELS; + + if(pipecb_f) + (*pipecb_f)(syspipe, OSB_PRE_OPEN, NULL); + + rv = mswin_exec_and_wait(whatsit, syspipe->command, + syspipe->infile, syspipe->outfile, + &syspipe->exit_code, flags); + + if(pipecb_f) + (*pipecb_f)(syspipe, OSB_POST_OPEN, (void *)rv); + + return rv; +} +#endif |