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.22' '-r1.23' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_pipe.c,v' 2>/dev/null --- l2_ch_pipe.c 2001/10/01 16:26:14 1.22 +++ l2_ch_pipe.c 2001/10/02 15:51:32 1.23 @@ -33,18 +33,23 @@ #include #include #include +#include -#define L2_PIPE_MODEDIRECT 1 /* direct command execution */ -#define L2_PIPE_MODESHELL 2 /* shell command execution */ -#define L2_PIPE_WRITEFAIL 6 /* how long before real failure is indicated */ -#define L2_PIPE_MAXARGS 256 /* how many args can our piped command have */ +#define L2_PIPE_EXECMODE_DIRECT 1 /* direct command execution */ +#define L2_PIPE_EXECMODE_SHELL 2 /* shell command execution */ +#define L2_PIPE_RUNTIME_RELIABLE 3 /* reliable pipe command processing */ +#define L2_PIPE_RUNTIME_ONESHOT 4 /* oneshot pipe command processing */ +#define L2_PIPE_WRITEFAIL 6 /* how long before failure occurs */ +#define L2_PIPE_MAXARGS 256 /* how many args can piped command have */ /* declare private channel configuration */ typedef struct { pid_t Pid; /* process id of child command */ int iWritefail; /* counter to failed write() operations */ int piFd[2]; /* pipe file descriptor */ + int iNulldev; /* null device file descriptor */ int iMode; /* execution mode direct or shell */ + int iRtme; /* runtime mode reliable or oneshot */ 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 */ @@ -80,10 +85,8 @@ * * The option 'oneshot' describes subsequent behavior which also rebuilds * such a severed pipe. The difference is that the pipe handler will not - * spawn the child process until writing actually occurs. It will also - * terminate the child process after writing is finished, independant of - * whether the write operation succeeded or failed after the user-specified - * number of retries. + * spawn the child process until writing actually occurs. The channel will + * also block until the child process finishes and terminates on its own. * * The lifecycle of a pipe command process is illustrated in the * following figure: @@ -98,41 +101,43 @@ * Last-modified: 2000-09-28/14:40 * Name: l2_ch_pipe * Version: eo/1.0 - * H4sIALKVuDsCA61Z32/cNgx+3v0VAvqwDWgO+mnLz8U2FMiwYW3f9uL4lJ43n+9m - * +5rlvx9J2Sc5bRrp1qZxRdvfJ9KkSEp99fPbX5jays1t3e/Gpj65zRvXT27Y/Oqm - * oW02t24CibGN4HzL+eZd23/s3OZGboTknMkNZ0qyVw392RRM6tIwWcIjIQQOK2k2 - * mnH4MXgRBRPwL1DBK5oBCdPwKw1erOWsGVw9uT85F1+Dwc1CG6YUTkGwY3/ffjwP - * LyJhHgVIrYoZeTy5PhFklJi1fBjaKXWqQi6opjuOqaYV1bWmldUVptnqGtOqcpkq - * 1TQjDdwrFm/v3DgNx0cC3hQYPwUghCzgwm0Fb5UV3wimkC0Qy5kV73l2Cjm6FBL/ - * BlmWBV5EmUIDk3L/9swT3YAhMSmdwqTNWqEga5OhUKFQUmahiWSFH0kVSTRWrLUJ - * shUZ2sCUK5ogqyKDxui1UUE2OsOoUq61CXIpM7Sp+FqbIFc8Q5uqekJzkatqoZFE - * gSuk9DQ3M5wtAs4iNt9F8Ru+cRxCIQpWI3wK6Ni9QYvVyOfoOboBFFaf5mZZfcbY - * 9NVndLVafSTT6jOGZ6w+fHu1+uiGX33GSJ0cqJFCQaZATVWIAjOiCTIFaioNuSui - * CTLlglQaiu+IJsgU76k0FBIRTZApF6TS0DLRQq+WDcm0bLQw6csmprnItGxSaSj6 - * I5pIVgtN0uqTcll9c7gEV61GxK0rE4/wKaDjKLloUZp4NHuNxyN8CmgoqfObNJod - * w+MRPbXGXL53QFf6cjf6kAt6Xk+oJq13IFNQj7XGmquU78PiCs7XBRxftrRQCWeg - * RfnQ76A/7I/Doe5Y0w7N+TBOdd+48fXTduBzMikCGfLW7Fay39uTY2/2dd+7jj20 - * 054Nrmvru7Zrp8cUTrVwSg7dxnQeerdjxx64ug40nVjv4MZ0ZFP9t2N1/xIpByZZ - * mYVUGuzJhsE1U/sJ8PAPkAPdoW77CX5ZO42s2bfdLkXb8kKMrXNzPBygFWen4Qif - * cNyyn/6tD6fOsXF/fOi3oVHyGC0RLgtqTo15wXVluXwZreGhd914ck37/3ynC7h8 - * E99xu3wNjen+ie8GB+1ifde1456+8QlmSyCFDzOTGuTfnQfYxIC+9139AOzU8GKv - * PNTkyd3ZgTcTeKE4Lry4Mah9E7zzrmdtfzpPpOLzTqR6W1gyGuutVWV6vbXSruot - * yVRvrawy6i2+vaq3dMPXW6tEer2NFAoy1dtUhSizRjRBpkKZSkNpN6IJMpXtVBqq - * IRFNJKsMGkruEU2QqWyn0lDmj2iCTGU7lYaKRUQTZCrbyTTVE5qLTGWbaNK6XbXU - * 2zlcgseD01ajooIEb6zxI6y3OJ6/RDBmNSIM1cYwwqeAXmLermpiYakm4tmEFdq8 - * lAqLJRPQtv393rHzCLn1UD9C0vrn3A6QYHp3A+t/Ss1aMOvMSQFz5/b1p/Y4bNlb - * KDb7FipMPbrXMHSQcCjhfj8m0Ep+odVQHetxPEL6nzBtzaXnUibHU/3Qp+TXS1mh - * juXcT21HWRXT7J372PZQx96TnpgY59KWVWHIsfdtD5nfjQv3F/PqC9ari/UWHEWm - * Tm44tD18AvbXeZxYfY8HXtFnjdI1ln4MHSH89gwCwwe5YBaDXDzXVGKMI9hv62aa - * l7HCY3EgthjG+BRPX7a0zQuUi0Lck+KPkhGrfFYj2hF6OA4jQ5XUyxMDzUWmpX73 - * t/B8G1N9DyuW5jzXVGqGPZy65WBqwdXyxCqZa6ovvAvPtzGVOGeNMDVlmuqzXDVn - * PrQ08QzWFOU1Z7Aeds0ZLCFzz2AJlHNQSceb2Sg63vSmZR9vEiw+3kw8KM5WkipO - * NooOibNQlxCx2EPlh4iHXRMihMwNEQJlH9N7JXOP6a8yjTyQaxrFcbZpFMdZpoU4 - * JtjTOLYxUGxNyW00HfUYBY5gtxYp+TyKDo95iQP6ktSIfBm3Ms7gzk7gdKTmcIbW - * qD18bqD93EBEkn2Q7xHYQ0PxEg5XAJYQwgmoRdAl5cxFwfJ0LhtwMsJhScEDAVPR - * GRZMtmyvf4NG8h02krftvWsem87l+EWhNtl+Uba8zi+UYq7xC7Yc1/iFtgiZftHC - * 5vhFcT37hXYri1/+oJMO6EYvfmE/DA67/R9zHIRrNds/uIm5yj/0v3DX+Aeb32v8 - * Q0cemf4RqsryD57YkX9g2/BV9/hjS++e/wBMybpdCx8AAA== + * H4sIAIW5uTsCA61ZUW/cNgx+3v0KAX3YCjSBJEuy/VxsQ4EMG9bubS+Oo+SM+uyb + * 7WuWfz+Ssi352iTSrW3jir7jJ0qk+FHMm18+/Mqya7m7qbq7sa6OdvfedpMddr/Z + * aWjq3Y2dQGJsJzi/5nz3sekeWru7kjshOWdyx1km2Zua/uwMk0ZqprjmTPAi10yD + * vBMsY/ACfjjT+JDwADR6h//DmxweWpWcGYn/vCxzAyPN8xgYmJS7b884wQsYEpJU + * MUiZ2Rrk5cwkGKTVFsbLWiXAKL2F8bLSCTC53MJ4OZcJMIXYwni5EAkwJa1MqAXG + * yyU3ONJxMOUZzCqXZQKMybYwgZwtMJIgFPzkDuZqVmeLgNBy90MQvz5wQqct2KrU + * 4Qg/Be0wSlYrch2OZq/xcISfgnZulm/SaHYMD0f0aaH1ut9eu1Tr22AjF+35PKGZ + * VwbOe1nCIRcKDr2QGQwlYO6U32fUWIOYiQy+V9A55TjUoPFXdwcJpuuHQ9Wyuhnq + * 02Gcqq6247u/ORcvg0nhwRC3YjeS/dEcLXu/r7rOtuyxmfZssG1T3TZtMz3FYGYL + * puSwsOk0dPaO9R1gtS1YOrHOwoupZ1P12bKqew2UA5Is9QIq4Tt1Pwy2npovoA// + * ATjAHaqmm+CHNdPI6n3T3sVYm6/ACoZ1fzhALmfHoYctHK/Zz/9Wh2Nr2bjvH7tr + * AvSeUxK1pcFMDZH2iufyfNkYpeBD57nxaOvm/7lOGXh8F9fxYtkMhcn+zHWDtWDf + * bduMe9riI8wWAZrpBVQj/t1pABIEe+/b6hHQH4dmsqw/2qEiR96dLDgzAlcXywbo + * TGDs1m0/AiB5njXd8TSRic/7kEIpk3j6BCUcPH0uSQlWYJISzyUpzFFyyQ4zjopQ + * Fk4ZB+Ia0yZ+CiFE1UGIOVuEmQJB8S9UCh5VPmsSpRynTjnJLdVQjqFgx8JCFrDU + * 2MKCHkFhMT8MPkRCYYHf3hQW9MIVFjLPVDSVBwZ5mag81iCiJwijDV05GelKZCae + * ygNrvExUHmsNkVwA42WqlmJhiO2CRXmZqqXYRREZBtZ4mcqcWGuIFQNrvEz1Saw1 + * RJ4hzCpTfUIwUYWFEEthMe+sD5zNaI6CzQg/Be3Qvd6KzYg+XaKbz6fPpSlT4MEE + * itegrvRr+d0s6c0g1XzaW3YagTAO1RNk4n9OzQBZs7NXkNSm2FQMs86Y5NNbu6++ + * NP1wzT4Age4bYM1qtO9gaCGLEov8OEbASr7CYilTjWMPnDZhLp7pdKX+8Vg9dhGQ + * rlgiSExXt21ffx7ZqZuadjYPk/zM0gzuWIemgxmjrDWLteTE+6E/EJ0BN7C2f2jq + * bxLGKxynVnOxZnR2IrMh1d3ah6aDUuKT31ZcQ3dGRFg4urQt81wn8pCL6xnm+9CQ + * i2SxHJhUGqIj4dRxOC8Ua6wZGCsvWcrNORBmu7eQDhT8SI2PAgi/Hiy4+dwj52p4 + * bCHUMyqpSa3v7puH0/CqJsyTgabKzKwJhUkXqURlCClRTROpZeSiRfVL5NIoJ1y0 + * tLy8YGlFecnSynyZKnZpGqsUbhZv30HBOfRPwUHBQNAaq2lBd+VcRMePNvkl8ePU + * Lokf0kyNH1JK2WRyTbIWucYtLdk1pBa6JjLIk42kIE/WogA/03IsTMaXBZX7eJWH + * oojwihBPXOucFwEe0ZvBEdx+Aiue16Kajec4MHzhwG/rbazHykAInA5NY8MJWLk5 + * fL3u4mvnoCYur8jATaDYAfG8pocHA3mL9ARsCBB0ylzknvO5Cq8nA72Cu9yvS+oI + * wWTLdfV3qGE+Yg1z09zb+qluzz2m+OIwzAmvTJJht4AmAXJf5/iTbsFA6Osc7CfX + * Mnkb4c41CPDMvBADz/iSMtklvpS5usiX0ohkX4qsfN6XL0Q4VrqbCPd+y0S5OA5P + * Y6z1dGkJrY9wTIbrSD6dWZFfdjopuVziUSWKlBOTcTUHM11WXormweIV4G2KMUSj + * oTGeYSmLOIYtNI9mWMpa6Qzr1C5hWNJMZVhSSq7QnJGpFdpFSyMCS10alQHJS6My + * IGlpvgwgta8rNKz9DV+vDhhKiXeZQhZ8wcm+02WGMGeLYJh6mTFlNt/vcbS01GBv + * 3J2eF1hOZHl8S43sCVpqbs3YUitkmdBSw29vWmr0wrXUikzE/64uMMjL1H2KNYia + * JwGMl6kzFwtD+T+A8TI1sWJhqMERwHiZmlixMNS0CmC8TE2saJjyDGaVqYkVC0MN + * iwDGy9RujIWhlhaFdND8dDI2P02pYltq6+/q5nDxHvdO24zcLPl8pECbxvOG8nDk + * jpzS4cid4jnOCzqH/wEp6AKCgx8AAA== * -----END EMBEDDED OBJECT----- */ @@ -150,7 +155,9 @@ cfg->iWritefail = 0; cfg->piFd[0] = -1; cfg->piFd[1] = -1; + cfg->iNulldev = -1; cfg->iMode = -1; + cfg->iRtme = -1; cfg->szCmdpath = NULL; memset(&cfg->sigchld, 0, sizeof(cfg->sigchld)); memset(&cfg->sigpipe, 0, sizeof(cfg->sigpipe)); @@ -165,22 +172,33 @@ 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[3]; + l2_param_t pa[4]; l2_result_t rv; - char *sz = NULL; + char *szMode = NULL; + char *szRel = NULL; /* feed and call generic parameter parsing engine */ - L2_PARAM_SET(pa[0], mode, CHARPTR, &sz); - L2_PARAM_SET(pa[1], path, STRING, &cfg->szCmdpath); - L2_PARAM_END(pa[2]); + L2_PARAM_SET(pa[0], execmode, CHARPTR, &szMode); /* mode direct or shell */ + L2_PARAM_SET(pa[1], runtime, CHARPTR, &szRel); /* reliable or oneshot */ + L2_PARAM_SET(pa[2], path, STRING, &cfg->szCmdpath); /* path of cmd */ + L2_PARAM_END(pa[3]); if ((rv = l2_util_setparams(pa, fmt, ap)) != L2_OK) return rv; - if (sz != NULL) { - if (strcmp(sz, "direct") == 0) - cfg->iMode = L2_PIPE_MODEDIRECT; - else if (strcmp(sz, "shell") == 0) - cfg->iMode = L2_PIPE_MODESHELL; + if (szMode != NULL) { + if (strcmp(szMode, "direct") == 0) + cfg->iMode = L2_PIPE_EXECMODE_DIRECT; + else if (strcmp(szMode, "shell") == 0) + cfg->iMode = L2_PIPE_EXECMODE_SHELL; + else + return L2_ERR_ARG; + } + + if (szRel != NULL) { + if (strcmp(szRel, "reliable") == 0) + cfg->iRtme = L2_PIPE_RUNTIME_RELIABLE; + else if (strcmp(szMode, "oneshot") == 0) + cfg->iRtme = L2_PIPE_RUNTIME_ONESHOT; else return L2_ERR_ARG; } @@ -189,10 +207,10 @@ } /********************************************************** - * parse_cmdpath: Helper method to hook_open * + * parse_cmdpath: Helper method to spawn_command * * Parses szBuf into an argv-style string vector szArgs * **********************************************************/ -static l2_result_t parse_cmdpath (char *szBuf, char *szArgs[]) { +static l2_result_t parse_cmdpath(char *szBuf, char *szArgs[]) { int iCnt = 0; if (szBuf == NULL) /* check for bad input before we */ @@ -213,55 +231,44 @@ return L2_ERR_ARG; } -/* open channel */ -static l2_result_t hook_open(l2_context_t *ctx, l2_channel_t *ch) +/************************************************************ + * spawn_command: Helper method to hook_open and hook_write * + * Forks a new process, and copies the command executable * + ************************************************************/ +static l2_result_t spawn_command(l2_ch_pipe_t *cfg) { - l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; char *pVec[L2_PIPE_MAXARGS]; - struct sigaction locact; - l2_result_t rv; char *sz = NULL; - - /* consistency check */ - if (cfg->szCmdpath == NULL) - return L2_ERR_USE; + l2_result_t rv; /* 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; - - if (pipe(cfg->piFd) == -1) /* open the pipe */ - return L2_ERR_SYS; + /* spawn a child process to be later overwritten by the user command */ if ((cfg->Pid = fork()) > 0) { /* parent process */ free(sz); /* no exec() in parent */ close(cfg->piFd[0]); /* half-duplex (no reading) */ cfg->piFd[0] = -1; + return L2_OK; } else if (cfg->Pid == 0) { /* child process */ 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 */ + /* redirection of child's stdout and stdin */ + cfg->iNulldev = open("/dev/null", O_RDWR); + dup2(cfg->iNulldev, fileno(stdout)); /* redirect stdout to null */ + dup2(cfg->iNulldev, fileno(stderr)); /* redirect stderr to null */ + /* the distinction between modes is necessary, because only executing */ /* commands in a shell environment allows usage of variables and such */ - if (cfg->iMode == L2_PIPE_MODESHELL) { + if (cfg->iMode == L2_PIPE_EXECMODE_SHELL) { pVec[0] = "/bin/sh"; pVec[1] = "-c"; pVec[2] = cfg->szCmdpath; pVec[3] = NULL; /* add a NULL to mark the end of the chain */ } - else { /* plain direct command execution */ sz = strdup(cfg->szCmdpath); if ((rv = parse_cmdpath(sz, pVec)) != L2_OK) { @@ -277,9 +284,44 @@ cfg->piFd[0] = -1; /* if execvp() doesn't swap our context or */ return L2_ERR_SYS; /* if child returns, we have an error */ } + else + return L2_OK; /* NOTREACHED */ } else /* fork failed */ return L2_ERR_SYS; +} + +/* 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; + struct sigaction locact; + + /* consistency check */ + if (cfg->szCmdpath == NULL) + return L2_ERR_USE; + + /* initialize auto vars before using them */ + 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; + + if (pipe(cfg->piFd) == -1) /* open the pipe */ + return L2_ERR_SYS; + + /* short circuit hack, if in oneshot mode and not yet opened then return */ + if ((cfg->iRtme == L2_PIPE_RUNTIME_ONESHOT) && (ch->state != L2_CHSTATE_OPENED)) + return L2_OK; + else + spawn_command(cfg); /* spawn the command process */ return L2_OK; } @@ -291,6 +333,10 @@ l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; l2_result_t rv; + /* we must still spawn the child command process if we are in oneshot mode */ + if ((cfg->iRtme == L2_PIPE_RUNTIME_ONESHOT) && (cfg->Pid != -1)) + spawn_command(cfg); + /* write message to channel pipe */ if (write(cfg->piFd[1], buf, buf_size) == -1) { if ((errno == EPIPE) && (cfg->iWritefail++ < L2_PIPE_WRITEFAIL)) { @@ -316,19 +362,33 @@ { l2_ch_pipe_t *cfg = (l2_ch_pipe_t *)ctx->vp; - /* restore previous signal context */ - if (sigaction(SIGCHLD, &cfg->sigchld, 0) < 0) - return L2_ERR_SYS; - if (sigaction(SIGPIPE, &cfg->sigpipe, 0) < 0) - return L2_ERR_SYS; + /* restore previous signal context, but only if it was saved and replaced */ + if (&cfg->sigchld.sa_handler) { + if (sigaction(SIGCHLD, &cfg->sigchld, 0) < 0) + return L2_ERR_SYS; + if (sigaction(SIGPIPE, &cfg->sigpipe, 0) < 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)) && (errno != ESRCH)) - return L2_ERR_SYS; + /* close null device */ + if (cfg->iNulldev != -1) { + close(cfg->iNulldev); + cfg->iNulldev = -1; + } + + /* close output pipe for parent */ + if (cfg->piFd[1] != -1) { + close(cfg->piFd[1]); + cfg->piFd[1] = -1; + } + + /* kill child process if already started */ + if (cfg->Pid != -1) { + if ((kill (cfg->Pid, SIGTERM)) && (errno != ESRCH)) + return L2_ERR_SYS; + cfg->Pid = -1; + } - cfg->Pid = -1; return L2_OK; }