Index: ossp-pkg/l2/l2_ch_pipe.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ch_pipe.c,v rcsdiff -q -kk '-r1.13' '-r1.14' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_pipe.c,v' 2>/dev/null --- l2_ch_pipe.c 2001/09/20 16:26:56 1.13 +++ l2_ch_pipe.c 2001/09/21 17:32:08 1.14 @@ -28,6 +28,7 @@ */ #include "l2.h" +#include "l2_p.h" /* for TRACE() */ #include #include @@ -37,55 +38,36 @@ #define L2_PIPE_MODESHELL 2 /* shell command execution */ #define L2_PIPE_MAXARGS 256 /* shell command execution */ -static l2_result_t hook_open(l2_context_t *, l2_channel_t *); /* prototypes */ -static l2_result_t hook_close(l2_context_t *, l2_channel_t *); +static l2_result_t hook_close(l2_context_t *, l2_channel_t *); /* prototypes */ /* declare private channel configuration */ typedef struct { - int piFd[2]; /* pipe file descriptor */ - int iChild; /* exception status of child pipe process */ - int iMode; /* execution mode direct or shell */ - pid_t Pid; /* pid set during fork in hook_open() */ - char *szCmdpath; /* path to command and arguments */ + pid_t Pid; /* process id of child command */ + int iWritefail; /* counter to failed write() operations */ + int piFd[2]; /* pipe file descriptor */ + int iMode; /* execution mode direct or shell */ + char *szCmdpath; /* path to command and arguments */ + struct sigaction sigchld; /* initial state of chld signal handler */ + struct sigaction sigpipe; /* initial state of pipe signal handler */ } l2_ch_pipe_t; static void catchsignal(int sig, ...) { - va_list ap = NULL; - static l2_context_t *ctx = NULL; - static l2_channel_t *chan = NULL; - - if (sig == 0) { - va_start(ap, sig); - ctx = va_arg(ap, l2_context_t *); - chan = va_arg(ap, l2_channel_t *); - va_end(ap); - } - else if (sig == SIGCHLD) { -/* TRACE("SIGCHLD caught\n");*/ - waitpid(((l2_ch_pipe_t *)ctx->vp)->Pid, &((l2_ch_pipe_t *)ctx->vp)->iChild, WUNTRACED); - if (WIFEXITED(((l2_ch_pipe_t *)ctx->vp)->iChild)) { - close(((l2_ch_pipe_t *)ctx->vp)->piFd[1]); - ((l2_ch_pipe_t *)ctx->vp)->piFd[1] = -1; - ((l2_ch_pipe_t *)ctx->vp)->Pid = -1; - /* check if process called exit() abnormally, then if so restarts */ - if (WEXITSTATUS(((l2_ch_pipe_t *)ctx->vp)->iChild)) { - fprintf(stderr, "exit status is %d\n", WEXITSTATUS(((l2_ch_pipe_t *)ctx->vp)->iChild)); - if (hook_open(ctx, chan) != L2_OK) { /* TODO: Fix infinit loop! */ - close(((l2_ch_pipe_t *)ctx->vp)->piFd[1]); - ((l2_ch_pipe_t *)ctx->vp)->piFd[1] = -1; - } - } - } - else if (WIFSTOPPED(((l2_ch_pipe_t *)ctx->vp)->iChild)) { - } - } - else if (sig == SIGPIPE) { /* thrown when we write to child's closed pipe */ -/* TRACE("SIGPIPE caught\n");*/ - close(((l2_ch_pipe_t *)ctx->vp)->piFd[1]); - ((l2_ch_pipe_t *)ctx->vp)->piFd[1] = -1; + pid_t Pid; /* for wait() */ + int iStatus = 0; /* for wait() */ + + if (sig == SIGCHLD) { + TRACE("SIGCHLD caught\n"); + Pid = waitpid(-1, &iStatus, WUNTRACED); + if (WIFEXITED(iStatus)) + TRACE("EXITED child\n"); /* child finished and returned */ + else if (WIFSIGNALED(iStatus)) + TRACE("SIGNALED child\n"); /* child finished due to a signal */ + else if (WIFSTOPPED(iStatus)) + TRACE("STOPPED child\n"); /* child stopped due to a signal */ } + else if (sig == SIGPIPE); /* noop for now */ } /* create channel */ @@ -98,12 +80,14 @@ return L2_ERR_ARG; /* initialize configuration with reasonable defaults */ - cfg->piFd[0] = -1; - cfg->piFd[1] = -1; - cfg->iChild = 0; - cfg->iMode = -1; - cfg->Pid = -1; - cfg->szCmdpath = NULL; + cfg->Pid = -1; + cfg->iWritefail = 0; + cfg->piFd[0] = -1; + cfg->piFd[1] = -1; + cfg->iMode = -1; + cfg->szCmdpath = NULL; + memset(&cfg->sigchld, 0, sizeof(cfg->sigchld)); + memset(&cfg->sigpipe, 0, sizeof(cfg->sigpipe)); /* link private channel configuration into channel context */ ctx->vp = cfg; @@ -135,10 +119,10 @@ else return L2_ERR_ARG; - /* check to see if a file exists at the user specified path */ - if(cfg->iMode != L2_PIPE_MODESHELL) { + /* check to see if a file exists at the user specified path */ + if (cfg->iMode != L2_PIPE_MODESHELL) { szTemp = strdup(cfg->szCmdpath); - for (pbIndex = szTemp; *pbIndex != NULL; pbIndex++); + for (pbIndex = szTemp; (*pbIndex != ' ') && (*pbIndex != NULL); pbIndex++); *pbIndex = NULL; if (!(File = fopen(szTemp, "r"))) return L2_ERR_ARG; /* the command does not exist at the given path */ @@ -149,11 +133,7 @@ pbIndex = NULL; } - catchsignal(0, ctx, ch); /* initialize signal handler with context & ch */ - signal(SIGCHLD, (void(*)())catchsignal); /* pipe changes descriptor */ - signal(SIGPIPE, (void(*)())catchsignal); /* pipe closes reading fd */ - - return rv; /* all okay */ + return rv; } /********************************************************** @@ -163,6 +143,9 @@ static l2_result_t parse_cmdpath (char *szBuf, char *szArgs[]) { int iCnt = 0; + if (szBuf == NULL) /* check for bad input before we */ + return L2_ERR_ARG; /* dereference and throw a SIGSEV */ + while ((iCnt++ < L2_PIPE_MAXARGS) && (*szBuf != NULL)) { while ((*szBuf == ' ') || (*szBuf == '\t')) *szBuf++ = '\0'; /* overwrite whitespace with EOL */ @@ -183,13 +166,22 @@ { l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; char *pVec[L2_PIPE_MAXARGS]; + struct sigaction locact; l2_result_t rv; - /* make sure a command path was set */ - if (cfg->szCmdpath == NULL) - return L2_ERR_USE; - + /* initialize auto vars before using them */ memset(pVec, 0, sizeof(pVec)); + memset(&locact, 0, sizeof(locact)); + + locact.sa_handler = (void(*)())catchsignal; + sigemptyset(&locact.sa_mask); + locact.sa_flags = 0; + + /* save old signal context before replacing with our own */ + if (sigaction(SIGCHLD, &locact, &cfg->sigchld) < 0) + return L2_ERR_SYS; + if (sigaction(SIGPIPE, &locact, &cfg->sigpipe) < 0) + return L2_ERR_SYS; /* the distinction between modes is necessary, because only executing */ /* commands in a shell environment allows usage of variables and such */ @@ -200,7 +192,7 @@ pVec[3] = NULL; /* add a NULL to mark the end of the chain */ } - else /* plain command execution */ + else /* plain direct command execution */ if ((rv = parse_cmdpath(cfg->szCmdpath, pVec)) != L2_OK) return rv; @@ -217,8 +209,8 @@ dup2(cfg->piFd[0], fileno(stdin)); /* copy the reading end */ if (execvp(*pVec, pVec) == -1) { /* launch */ - close(cfg->piFd[0]); /* cleanup */ - cfg->piFd[0] = -1; + close(cfg->piFd[0]); /* cleanup in case we fail */ + cfg->piFd[0] = -1; /* if execvp() doesn't swap our context or */ return L2_ERR_SYS; /* if child returns, we have an error */ } } @@ -228,27 +220,28 @@ return L2_OK; } -/* write to channel */ +/* write to channel, possibly recursively */ static l2_result_t hook_write(l2_context_t *ctx, l2_channel_t *ch, l2_level_t level, const char *buf, size_t buf_size) { l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; - if (cfg->piFd[1] == -1) - return L2_ERR_IO; - - if (WIFSTOPPED(cfg->iChild)) { - if (kill(cfg->Pid, SIGCONT)) { + /* write message to channel pipe */ + if (write(cfg->piFd[1], buf, buf_size) == -1) { + if ((errno == EPIPE) && (cfg->iWritefail++ < 6)) { hook_close(ctx, ch); - cfg->iChild = 0; + hook_open(ctx, ch); + return hook_write(ctx, ch, level, buf, buf_size); + } + else { /* not broken pipe problem or over the fail limit */ + cfg->iWritefail = 0; /* reset pipe failure counter */ return L2_ERR_SYS; } } - /* write message to channel pipe */ - else if (write(cfg->piFd[1], buf, buf_size) == -1) - return L2_ERR_SYS; - - return L2_OK; + else { /* write() to pipe succeeded */ + cfg->iWritefail = 0; /* reset pipe failure counter */ + return L2_OK; + } } /* close channel */ @@ -256,13 +249,19 @@ { l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; + /* restore previous signal context */ + if (sigaction(SIGCHLD, &cfg->sigchld, NULL) < 0) + return L2_ERR_SYS; + if (sigaction(SIGPIPE, &cfg->sigpipe, NULL) < 0) + return L2_ERR_SYS; + /* close channel pipe for parent process created in hook_open() */ close(cfg->piFd[1]); cfg->piFd[1] = -1; - if (kill(cfg->Pid, SIGTERM)) + if ((kill (cfg->Pid, SIGTERM)) && (errno != ESRCH)) return L2_ERR_SYS; - cfg->Pid = -1; + cfg->Pid = -1; return L2_OK; }