Index: ossp-pkg/l2/TODO RCS File: /v/ossp/cvs/ossp-pkg/l2/TODO,v rcsdiff -q -kk '-r1.22' '-r1.23' -u '/v/ossp/cvs/ossp-pkg/l2/TODO,v' 2>/dev/null --- TODO 2001/09/14 13:27:17 1.22 +++ TODO 2001/09/18 14:38:51 1.23 @@ -25,6 +25,7 @@ MS: - implement pipe channel +- review pipe handler for dangling descriptors ISSUES ------ 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.10' '-r1.11' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_pipe.c,v' 2>/dev/null --- l2_ch_pipe.c 2001/09/14 15:40:04 1.10 +++ l2_ch_pipe.c 2001/09/18 14:38:51 1.11 @@ -32,24 +32,37 @@ #include #include +#define L2_PIPE_MODEDIRECT 1 /* direct command execution */ +#define L2_PIPE_MODESHELL 2 /* shell command execution */ +#define L2_PIPE_MAXARGS 256 /* shell command execution */ + /* declare private channel configuration */ typedef struct { - int iPipe; /* pipe filedescriptor used for writing only */ + int piFd[2]; /* pipe file descriptor */ + int iMode; /* execution mode direct or shell */ + char *szCmdpath; /* path to command and arguments */ + char *pVec[L2_PIPE_MAXARGS]; /* vector of command and params */ } l2_ch_pipe_t; static void catchsignal(int sig, ...) { va_list ap; - static l2_context_t *ctx = NULL; + static l2_ch_pipe_t *cfg = NULL; va_start(ap, sig); if (sig == 0) - ctx = va_arg(ap, l2_context_t *); + cfg = va_arg(ap, l2_ch_pipe_t *); else if (sig == SIGCHLD) { - ((l2_ch_pipe_t *)(int *)ctx->vp)->iPipe = -1; /* TODO: Fill in with new pipe fd */ + fprintf(stderr, "Pipe: SIGCHLD caught\n"); + close(cfg->piFd[1]); + cfg->piFd[1] = -1; /* TODO: Fill in with new pipe fd */ + } + else if (sig == SIGPIPE) { + fprintf(stderr, "Pipe: SIGPIPE caught\n"); + close(cfg->piFd[1]); + cfg->piFd[1] = -1; /* TODO: Fill in with new pipe fd */ + /* ((l2_ch_pipe_t *)(int *)ctx->vp)->piFd[1] = -1; */ } - else if (sig == SIGPIPE) - ((l2_ch_pipe_t *)(int *)ctx->vp)->iPipe = -1; va_end(ap); } @@ -63,7 +76,11 @@ return L2_ERR_ARG; /* initialize configuration with reasonable defaults */ - cfg->iPipe = -1; + cfg->piFd[0] = -1; + cfg->piFd[1] = -1; + cfg->iMode = -1; + cfg->szCmdpath = NULL; + memset(cfg->pVec, 0, sizeof(cfg->pVec)); /* link private channel configuration into channel context */ ctx->vp = cfg; @@ -75,45 +92,114 @@ static l2_result_t hook_configure(l2_context_t *ctx, l2_channel_t *ch, const char *fmt, va_list ap) { l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; - l2_param_t pa[2]; + l2_param_t pa[3]; l2_result_t rv; + char szMode[16]; /* just eneough for double-byte "direct" and NULL */ /* feed and call generic parameter parsing engine */ - L2_PARAM_SET(pa[0], pipe, INT, &cfg->iPipe); - L2_PARAM_END(pa[1]); + L2_PARAM_SET(pa[0], mode, CHARPTR, szMode); + L2_PARAM_SET(pa[1], path, STRING, &cfg->szCmdpath); + L2_PARAM_END(pa[2]); rv = l2_util_setparams(pa, fmt, ap); - /* validate the pipe fd */ - if (cfg->iPipe == -1) + if (strcmp(szMode, "direct") == NULL) + cfg->iMode = L2_PIPE_MODEDIRECT; + else if (strcmp(szMode, "shell") == NULL) + cfg->iMode = L2_PIPE_MODESHELL; + else return L2_ERR_ARG; - catchsignal(0, ctx); /* initialize signal handler with incoming context */ + catchsignal(0, cfg); /* initialize signal handler with incoming context */ signal(SIGCHLD, (void(*)())catchsignal); /* pipe changes descriptor */ signal(SIGPIPE, (void(*)())catchsignal); /* pipe closes reading fd */ return rv; } +/********************************************************** + * parse_cmdpath: Helper method to hook_open * + * Parses szBuf into an argv-style string vector szArgs * + **********************************************************/ +static l2_result_t parse_cmdpath (char *szBuf, char *szArgs[]) { + int iCnt = 0; + while ((iCnt++ < L2_PIPE_MAXARGS) && (*szBuf != NULL)) { + while ((*szBuf == ' ') || (*szBuf == '\t')) + *szBuf++ = '\0'; + *szArgs++ = szBuf; + while ((*szBuf != '\0') && (*szBuf != ' ') && (*szBuf != '\t')) + szBuf++; + } + *szArgs = NULL; + + if (iCnt <= L2_PIPE_MAXARGS) + return L2_OK; + else + return L2_ERR_ARG; +} + +/********************************************************** + * child_exec: Helper method to hook_open * + * Redirect stdin to read from cfg->piFd. Exec a given * + * command or passes it to the sh shell for execution. * + **********************************************************/ +static l2_result_t child_exec(l2_ch_pipe_t *cfg) +{ + /* make sure a command path was set */ + if (cfg->szCmdpath == NULL) + return L2_ERR_USE; + + close(cfg->piFd[1]); /* close the writing end, */ + cfg->piFd[1] = -1; /* because we don't use it */ + dup2(cfg->piFd[0], fileno(stdin)); /* copy the reading end */ + + return execvp(*cfg->pVec, cfg->pVec); /* launch */ +} + +/* open channel */ +static l2_result_t hook_open(l2_context_t *ctx, l2_channel_t *ch) +{ + l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; + l2_result_t rv; + pid_t Pid; + + if ((rv = parse_cmdpath(cfg->szCmdpath, cfg->pVec)) != L2_OK) + return rv; + + if (pipe(cfg->piFd) == -1) /* open the pipe */ + return L2_ERR_SYS; + + if ((Pid = fork()) > 0) { /* parent process */ + close(cfg->piFd[0]); + cfg->piFd[0] = -1; + } + else if ((Pid = fork()) == 0) { /* child process */ + /* if child returns, there was an error */ + if (child_exec(cfg) == -1) { + close(cfg->piFd[0]); + cfg->piFd[0] = -1; + return L2_ERR_SYS; + } + } + else /* fork failed */ + return L2_ERR_SYS; + + return L2_OK; +} + /* write to channel */ 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; - l2_channel_t *downstream = l2_channel_downstream(ch); - l2_result_t rv; - + /* validate the pipe fd */ - if (cfg->iPipe == -1) - return L2_ERR_ARG; + if (cfg->piFd[1] == -1) /* TODO: possibly remove this validation in */ + return L2_ERR_ARG; /* lieu of incoming SIGPIPE signal */ /* write message to channel pipe */ - if (write(cfg->iPipe, buf, buf_size) == -1) + if (write(cfg->piFd[1], buf, buf_size) == -1) return L2_ERR_SYS; - /* write to downstream channel */ - if ((rv = l2_channel_write(downstream, level, buf, buf_size)) != L2_OK) - return rv; - return L2_OK; } @@ -121,16 +207,10 @@ static l2_result_t hook_close(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; - l2_channel_t *downstream = l2_channel_downstream(ch); - l2_result_t rv; - - /* close channel pipe */ - close(cfg->iPipe); - cfg->iPipe = -1; - /* close downstream channel, too */ - if ((rv = l2_channel_close(downstream)) != L2_OK) - return rv; + /* close channel pipe for parent process created in hook_open() */ + close(cfg->piFd[1]); + cfg->piFd[1] = -1; return L2_OK; } @@ -139,25 +219,21 @@ static l2_result_t hook_destroy(l2_context_t *ctx, l2_channel_t *ch) { l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; - l2_channel_t *downstream = l2_channel_downstream(ch); - l2_result_t rv; /* destroy channel configuration */ + free(&cfg->szCmdpath); + cfg->szCmdpath = NULL; free(cfg); - /* destroy downstream channel, too */ - if ((rv = l2_channel_destroy(downstream)) != L2_OK) - return rv; - return L2_OK; } /* exported channel handler structure */ l2_handler_t l2_handler_pipe = { - L2_CHANNEL_FILTER, + L2_CHANNEL_OUTPUT, hook_create, hook_configure, - NULL, + hook_open, hook_write, NULL, hook_close, Index: ossp-pkg/l2/l2_test.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_test.c,v rcsdiff -q -kk '-r1.23' '-r1.24' -u '/v/ossp/cvs/ossp-pkg/l2/l2_test.c,v' 2>/dev/null --- l2_test.c 2001/09/14 19:11:00 1.23 +++ l2_test.c 2001/09/18 14:38:51 1.24 @@ -34,7 +34,7 @@ #include #include #include -#include +#include /* unistd and fcntl included for pipes */ #include #include "l2.h" @@ -74,84 +74,42 @@ #ifdef WITH_PIPE l2_channel_t *chPipe; #endif -#ifdef WITH_SOCKET l2_channel_t *chSock; -#endif #ifdef WITH_SMTP l2_channel_t *chSmtp; #endif l2_stream_t *st; - int pipdesc[2]; /* For use with our pipe channel test */ /* * Typical steps to use a buffered file logging stream */ - if ((st = l2_stream_create()) == NULL) die("failed to create stream"); -#ifdef WITH_SOCKET - /* Atenzione! Before doing any socket testing, make sure you have a valid */ - /* end point listening, or else you will only get an error message when */ - /* the thing tries to connect. */ - if ((chSock = l2_channel_create(&l2_handler_socket)) == NULL) - die("failed to create socket channel"); - - if (l2_channel_configure(chSock, "protocol,ipversion,host,port",\ - IPPROTO_TCP, AF_INET, "localhost", 2002) != L2_OK) - die("failed to configure socket ipv4 channel"); -#if 0 - if (l2_channel_configure(chSock, "protocol,ipversion,host,port",\ - IPPROTO_TCP, AF_INET6, "0:0:0:0:0:0:0:1", 2002) != L2_OK) - die("failed to configure socket ipv6 channel"); -#endif - if (l2_channel_open(chSock) != L2_OK) - die("failed to open socket channel"); - - if (l2_stream_channel(st, chSock, L2_LEVEL_UPTO(L2_LEVEL_WARNING), L2_LEVEL_NONE) != L2_OK) - die("failed to attach first channel into stream"); -#endif - if ((chPrefix = l2_channel_create(&l2_handler_prefix)) == NULL) /* Prefix */ die("failed to create prefix channel"); if (l2_channel_configure(chPrefix, "prefix,timezone", "[%d-%m-%Y/%H:%M:%S] %L test[%P]: ", "local") != L2_OK) die("failed to configure prefix channel"); - if ((chBuf = l2_channel_create(&l2_handler_buffer)) == NULL) /* Buffer */ + if ((chBuf = l2_channel_create(&l2_handler_buffer)) == NULL) /* Buffer */ die("failed to create buffer channel"); if (l2_channel_configure(chBuf, "size", 100) != L2_OK) die("failed to configure buffer channel"); - if ((chFile = l2_channel_create(&l2_handler_file)) == NULL) /* File */ + if ((chFile = l2_channel_create(&l2_handler_file)) == NULL) /* File */ die("failed to create file channel"); if (l2_channel_configure(chFile, "path,append,perm", "l2_test.log", TRUE, 0644) != L2_OK) die("failed to configure file channel"); -#ifdef WITH_PIPE - if (pipe(pipdesc)) - die("pipe() call failed"); - - if ((chPipe = l2_channel_create(&l2_handler_pipe)) == NULL) /* Pipe */ - die("failed to create pipe channel"); - - if (l2_channel_configure(chPipe, "pipe", pipdesc[1]) != L2_OK) - die("failed to configure pipe channel"); -#endif - if (l2_channel_stack(chFile, chBuf) != L2_OK) die("failed to stack buffer channel on top of file channel"); if (l2_channel_stack(chBuf, chPrefix) != L2_OK) die("failed to stack prefix channel on top of buffer channel"); -#ifdef WITH_PIPE - if (l2_channel_stack(chPrefix, chPipe) != L2_OK) - die("failed to stack pipe channel on top of prefix channel"); -#endif - if (l2_channel_open(chPrefix) != L2_OK) die("failed to open channel stack"); @@ -159,7 +117,7 @@ die("failed to attach channel stack into stream"); #ifdef WITH_SYSLOG - if ((chSyslog = l2_channel_create(&l2_handler_syslog)) == NULL) /* Syslog */ + if ((chSyslog = l2_channel_create(&l2_handler_syslog)) == NULL) /* Syslog */ die("failed to create syslog channel"); if (l2_channel_configure(chSyslog, "ident,facility,target,remotehost,logpid", @@ -173,8 +131,44 @@ die("failed to attach channel syslog into stream"); #endif +#ifdef WITH_PIPE + if ((chPipe = l2_channel_create(&l2_handler_pipe)) == NULL) /* Pipe */ + die("failed to create pipe channel"); + + if (l2_channel_configure(chPipe, "pipe", pipdesc[1]) != L2_OK) + die("failed to configure pipe channel"); + + if (l2_channel_open(chPipe) != L2_OK) + die("failed to open pipe channel"); + + if (l2_stream_channel(st, chPipe, L2_LEVEL_UPTO(L2_LEVEL_WARNING), L2_LEVEL_NONE) != L2_OK) + die("failed to attach channel pipe into stream"); +#endif + +#ifdef WITH_SOCKET + /* Atenzione! Before doing any socket testing, make sure you have a valid */ + /* end point listening, or else you will only get an error message when */ + /* the thing tries to connect. */ + if ((chSock = l2_channel_create(&l2_handler_socket)) == NULL) /* Socket */ + die("failed to create socket channel"); + + if (l2_channel_configure(chSock, "protocol,ipversion,host,port",\ + IPPROTO_TCP, AF_INET, "localhost", 2002) != L2_OK) + die("failed to configure socket ipv4 channel"); +#if 0 + if (l2_channel_configure(chSock, "protocol,ipversion,host,port",\ + IPPROTO_TCP, AF_INET6, "0:0:0:0:0:0:0:1", 2002) != L2_OK) + die("failed to configure socket ipv6 channel"); +#endif + if (l2_channel_open(chSock) != L2_OK) + die("failed to open socket channel"); + + if (l2_stream_channel(st, chSock, L2_LEVEL_UPTO(L2_LEVEL_WARNING), L2_LEVEL_NONE) != L2_OK) + die("failed to attach channel socket into stream"); +#endif + #ifdef WITH_SMTP - if ((chSmtp = l2_channel_create(&l2_handler_smtp)) == NULL) /* SMTP */ + if ((chSmtp = l2_channel_create(&l2_handler_smtp)) == NULL) /* SMTP */ die("failed to create smtp channel"); if (l2_channel_configure(chSmtp, "rcpt,host,port", "rse@engelschall.com", "en1", "25") != L2_OK) @@ -202,8 +196,6 @@ if (l2_stream_destroy(st) != L2_OK) die("failed to destroy stream"); - close(pipdesc[1]); /* Close the locally made pipe file descriptor */ - close(pipdesc[0]); /* Close the locally made pipe file descriptor */ return 0; }