713 lines
24 KiB
C
713 lines
24 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2008, 2012 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
#define _REENTRANT
|
|
|
|
#include "mdef.h"
|
|
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <libgen.h>
|
|
#include "gtm_fcntl.h"
|
|
#include "gtm_unistd.h"
|
|
#include "gtm_stat.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_string.h"
|
|
#include "gtm_stdlib.h"
|
|
#include "gtm_limits.h"
|
|
#include "stringpool.h"
|
|
#include "io.h"
|
|
#include "iormdef.h"
|
|
#include "io_params.h"
|
|
#include "eintr_wrappers.h"
|
|
#include "gtmio.h"
|
|
#include "iosp.h"
|
|
#include "jobsp.h"
|
|
#ifdef __MVS__
|
|
#include "gtm_zos_io.h"
|
|
#include "gtm_zos_chset.h"
|
|
#endif
|
|
|
|
LITREF unsigned char io_params_size[];
|
|
ZOS_ONLY(GBLREF boolean_t gtm_tag_utf8_as_ascii;)
|
|
GBLREF boolean_t gtm_pipe_child;
|
|
|
|
error_def(ERR_DEVOPENFAIL);
|
|
error_def(ERR_SYSCALL);
|
|
error_def(ERR_TEXT);
|
|
ZOS_ONLY(error_def(ERR_BADTAG);)
|
|
|
|
#define FREE_ALL { if (NULL != copy_cmd_string) free(copy_cmd_string); if (NULL != temp) free(temp);\
|
|
if (NULL != buf) free(buf); if (NULL != dir_in_path) free(dir_in_path);if (NULL != command2) free(command2); }
|
|
|
|
int parse_pipe(char *cmd_string, char *ret_token);
|
|
|
|
/* The parse_pipe routine is to used to determine if all the commands in a pipe string are valid commands so
|
|
the iopi_open() routine can detect the problem prior to a fork.
|
|
|
|
Command Parsing Limitations:
|
|
|
|
All commands will be searched for in $PATH and in $gtm_dist and if not found will trap. Most pipe strings can be
|
|
entered with some minor exceptions. First, no parentheses around commands are allowed. An example of this which would
|
|
fail is:
|
|
|
|
o p:(comm="(cd; pwd)":writeonly)::"pipe"
|
|
|
|
This produces nothing different from:
|
|
|
|
o p:(comm="cd; pwd":writeonly)::"pipe"
|
|
|
|
This restriction does not include parentheses embedded in character strings as in:
|
|
|
|
o p:(comm="echo ""(test)""":writeonly)::"pipe" (which would output: (test) )
|
|
|
|
or as parameters to a command as in:
|
|
|
|
o p:(comm="tr -d '()'":writeonly)::"pipe" (which if passed (test) would output: test )
|
|
|
|
Second, commands must be valid commands - not aliases. Third, no shell built-in commands are allowed (unless a version
|
|
is found in the $PATH and $gtm_dist); with the exception of nohup and cd. In the case of nohup, the next token will be
|
|
the command to be looked for in $PATH and $gtm_dist. "cd" will be allowed, but no parsing for a command will occur
|
|
until the next "|" token - if there is one. In addition, environment variables may be used to define a path, or actual
|
|
paths may be given.
|
|
|
|
The following are examples of valid open commands:
|
|
|
|
1. o a:(comm="tr e j | echoback":stderr=e:exception="g BADOPEN")::"pipe"
|
|
2. o a:(comm="/bin/cat |& nl")::"pipe"
|
|
3. o a:(comm="mupip integ mumps.dat")::"pipe"
|
|
4. o a:(comm="$gtm_dist/mupip integ mumps.dat")::"pipe"
|
|
5. o a:(comm="nohup cat")::"pipe"
|
|
6. o p:(comm="cd ..; pwd | tr -d e":writeonly)::"pipe"
|
|
|
|
In the first example the commands parsed are "tr" and "echoback". "echoback" is in the current directory which is
|
|
included in the $PATH. In the second example an explicit path is given for "cat", but "nl" is found via $PATH.
|
|
In the third example mupip will be found if it is in the $PATH or in $gtm_dist. In the fourth example the
|
|
$gtm_dist environment variable is explicitly used to find mupip. In the fifth example the "cat" after "nohup"
|
|
is used as the command looked up in $PATH. In the sixth example the default directory is moved up a level and
|
|
pwd is executed with the output piped to the tr command. The pwd command is not checked for existence, but the
|
|
"tr" command after the "|" is checked.
|
|
*/
|
|
int parse_pipe(char *cmd_string, char *ret_token)
|
|
{
|
|
char *str1, *str2, *str3, *token;
|
|
char *saveptr1, *saveptr2, *saveptr3;
|
|
char *env_var;
|
|
int env_inc;
|
|
struct stat sb;
|
|
char *path, path_buff[GTM_PATH_MAX];
|
|
char *temp;
|
|
char *buf;
|
|
char *dir_in_path;
|
|
char *command2;
|
|
char *copy_cmd_string;
|
|
char *token1;
|
|
char *token2;
|
|
char *token3;
|
|
int notfound = FALSE;
|
|
int ret_stat;
|
|
int pathsize, path_len;
|
|
int cmd_string_size;
|
|
|
|
path = GETENV("PATH");
|
|
path_len = STRLEN(path);
|
|
if (GTM_PATH_MAX <= path_len)
|
|
path_len = GTM_PATH_MAX - 1;
|
|
memcpy(path_buff, path, path_len + 1); /* + 1 for null */
|
|
path = path_buff;
|
|
|
|
cmd_string_size = STRLEN(cmd_string) + 1;
|
|
pathsize = GTM_PATH_MAX + cmd_string_size;
|
|
buf = (char *)malloc(pathsize);
|
|
copy_cmd_string = (char *)malloc(cmd_string_size);
|
|
dir_in_path = (char *)malloc(GTM_PATH_MAX);
|
|
command2 = (char *)malloc(cmd_string_size);
|
|
temp = (char *)malloc(pathsize);
|
|
memcpy(copy_cmd_string, cmd_string, cmd_string_size);
|
|
|
|
/* guaranteed at least one token when we get here because it is checked right after iop_eol loop and
|
|
before this code is executed. */
|
|
for (str1 = copy_cmd_string; FALSE == notfound ; str1 = NULL)
|
|
{
|
|
/* separate into tokens in a pipe */
|
|
token1 = strtok_r(str1, "|", &saveptr1);
|
|
if (NULL == token1)
|
|
break;
|
|
|
|
/* separate into tokens using space as delimiter */
|
|
/* if the first token is a non-alpha-numeric or if it is nohup the skip it */
|
|
/* and use the next one as a command to find */
|
|
memcpy(command2, token1, STRLEN(token1) + 1);
|
|
|
|
for (str2 = command2; ; str2 = NULL)
|
|
{
|
|
token2 = strtok_r(str2, " >&;",&saveptr2);
|
|
if (NULL != token2 && !strcmp(token2, "cd"))
|
|
{
|
|
/* if the command is cd then skip the rest before the next pipe */
|
|
token2 = NULL;
|
|
break;
|
|
}
|
|
if (NULL != token2 && strcmp(token2, "nohup"))
|
|
break;
|
|
if (NULL == token2)
|
|
break;
|
|
}
|
|
|
|
if (NULL == token2)
|
|
continue;
|
|
|
|
notfound = TRUE;
|
|
if (NULL != strchr(token2, '/'))
|
|
{
|
|
/* if the first character is a $ sign then assume it is an environment variable used
|
|
like $gtm_dist/mupip. Get the environment variable and substitute it. */
|
|
notfound = TRUE;
|
|
if ('$' == *token2)
|
|
{
|
|
for (env_inc = 1; '/' != *(token2 + env_inc); env_inc++)
|
|
{
|
|
temp[env_inc - 1] = *(token2 + env_inc);
|
|
}
|
|
temp[env_inc] = '\0';
|
|
env_var = GETENV(temp);
|
|
if (NULL != env_var)
|
|
{
|
|
/* build a translated path to command */
|
|
assert(cmd_string_size > (STRLEN(token2 + env_inc) + STRLEN(env_var)));
|
|
SPRINTF(temp, "%s%s", env_var, token2 + env_inc);
|
|
|
|
/* The command must be a regular file and executable */
|
|
STAT_FILE(temp, &sb, ret_stat);
|
|
if (0 == ret_stat && (S_ISREG(sb.st_mode)) && (sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)))
|
|
notfound = FALSE;
|
|
}
|
|
} else
|
|
{
|
|
STAT_FILE(token2, &sb, ret_stat);
|
|
if (0 == ret_stat && (S_ISREG(sb.st_mode)) && (sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)))
|
|
notfound = FALSE;
|
|
}
|
|
} else
|
|
{
|
|
/* look in $gtm_dist in case not explicitly listed or not in the $PATH variable */
|
|
env_var = GETENV("gtm_dist");
|
|
if (NULL != env_var)
|
|
{
|
|
/* build a translated path to command */
|
|
SPRINTF(temp, "%s/%s", env_var, token2);
|
|
STAT_FILE(temp, &sb, ret_stat);
|
|
if (0 == ret_stat && (S_ISREG(sb.st_mode)) && (sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)))
|
|
notfound = FALSE;
|
|
else
|
|
notfound = TRUE;
|
|
}
|
|
|
|
/* search all the directories in the $PATH */
|
|
memcpy(dir_in_path, path, path_len + 1);
|
|
for (str3= dir_in_path; TRUE == notfound; str3 = NULL)
|
|
{
|
|
token3 = strtok_r(str3, ":", &saveptr3);
|
|
if (NULL == token3)
|
|
break;
|
|
SPRINTF(buf, "%s/%s", token3, token2);
|
|
notfound = TRUE;
|
|
/* The command must be a regular file and executable */
|
|
STAT_FILE(buf, &sb, ret_stat);
|
|
if (0 == ret_stat && (S_ISREG(sb.st_mode)) && (sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)))
|
|
{
|
|
notfound = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (TRUE == notfound)
|
|
{
|
|
assert(GTM_PATH_MAX > (STRLEN(token2) + 1));
|
|
memcpy(ret_token, token2, STRLEN(token2) + 1);
|
|
FREE_ALL;
|
|
return(FALSE);
|
|
}
|
|
}
|
|
FREE_ALL;
|
|
return(TRUE);
|
|
}
|
|
|
|
/* When we are in this routine dev_name is same as naml in io_open_try() */
|
|
|
|
#define PIPE_ERROR_INIT()\
|
|
{\
|
|
if (NULL != pcommand) free(pcommand);\
|
|
if (NULL != pshell) free(pshell);\
|
|
if (NULL != pstderr) free(pstderr);\
|
|
iod->type = rm;\
|
|
}
|
|
|
|
#define INVALID_CMD "Invalid command string: "
|
|
|
|
short iopi_open(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout)
|
|
{
|
|
io_desc *iod;
|
|
io_desc *stderr_iod;
|
|
d_rm_struct *d_rm;
|
|
io_log_name *stderr_naml; /* logical record for stderr device */
|
|
unsigned char ch;
|
|
int param_cnt = 0;
|
|
int p_offset = 0;
|
|
int file_des_write;
|
|
int file_des_read = 0;
|
|
int file_des_read_stderr;
|
|
int param_offset = 0;
|
|
struct stat sb;
|
|
int ret_stat;
|
|
int cpid;
|
|
int pfd_write[2];
|
|
int pfd_read[2];
|
|
int pfd_read_stderr[2];
|
|
enum pfield {PSHELL, PCOMMAND, PSTDERR};
|
|
int slen[3] = {0, 0, 0};
|
|
char *sparams[3] = {0, 0, 0};
|
|
char *pcommand = 0;
|
|
char *pshell = 0;
|
|
char *pshell_name;
|
|
char *pstderr = 0;
|
|
char *sh;
|
|
int independent = FALSE;
|
|
int parse = FALSE;
|
|
int status_read;
|
|
int return_stdout = TRUE;
|
|
int return_stderr = FALSE;
|
|
char ret_token[GTM_MAX_DIR_LEN];
|
|
char error_str[MAXDEVPARLEN + STR_LIT_LEN(INVALID_CMD)];
|
|
int save_errno;
|
|
int flags;
|
|
int fcntl_res, rc;
|
|
#ifdef __MVS__
|
|
gtm_chset_t read_chset, write_chset, stderr_chset = CHSET_EBCDIC;
|
|
int read_cvt[2], write_cvt[2], dummy_cvt[2];
|
|
mstr chset_mstr;
|
|
char *errmsg;
|
|
boolean_t textflag;
|
|
int ccsid, status, realfiletag;
|
|
#endif
|
|
|
|
iod = dev_name->iod;
|
|
|
|
while (iop_eol != *(pp->str.addr + p_offset))
|
|
{
|
|
assert((params) *(pp->str.addr + p_offset) < (params)n_iops);
|
|
switch ((ch = *(pp->str.addr + p_offset++)))
|
|
{
|
|
case iop_exception:
|
|
dev_name->iod->error_handler.len = *(pp->str.addr + p_offset);
|
|
dev_name->iod->error_handler.addr = (char *)(pp->str.addr + p_offset + 1);
|
|
s2pool(&dev_name->iod->error_handler);
|
|
break;
|
|
case iop_shell:
|
|
slen[PSHELL] = (unsigned int)(unsigned char)*(pp->str.addr + p_offset);
|
|
sparams[PSHELL] = (char *)(pp->str.addr + p_offset + 1);
|
|
param_cnt++;
|
|
break;
|
|
case iop_command:
|
|
slen[PCOMMAND] = (unsigned int)(unsigned char)*(pp->str.addr + p_offset);
|
|
sparams[PCOMMAND] = (char *)(pp->str.addr + p_offset + 1);
|
|
param_cnt++;
|
|
break;
|
|
case iop_stderr:
|
|
slen[PSTDERR] = (unsigned int)(unsigned char)*(pp->str.addr + p_offset);
|
|
sparams[PSTDERR] = (char *)(pp->str.addr + p_offset + 1);
|
|
param_cnt++;
|
|
break;
|
|
case iop_independent:
|
|
independent = TRUE;
|
|
break;
|
|
case iop_parse:
|
|
parse = TRUE;
|
|
break;
|
|
case iop_writeonly:
|
|
return_stdout = FALSE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ?
|
|
(unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]);
|
|
}
|
|
/* for z/OS, grab the chset from the device params to tag the pipe */
|
|
ZOS_ONLY(gtm_zos_iop_chset(dev_name, pp, &read_chset, &write_chset);)
|
|
|
|
/* If no command device parameter or it is a null string then exit with error */
|
|
if (0 == slen[PCOMMAND] || 0 == sparams[PCOMMAND])
|
|
{
|
|
PIPE_ERROR_INIT();
|
|
if (0 == sparams[PCOMMAND])
|
|
rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("Missing command string"));
|
|
else
|
|
rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("Command string has no value"));
|
|
} else
|
|
{
|
|
pcommand = malloc(slen[PCOMMAND] + 1);
|
|
memcpy(pcommand, sparams[PCOMMAND], slen[PCOMMAND]);
|
|
pcommand[slen[PCOMMAND]] = '\0';
|
|
if (TRUE == parse)
|
|
{
|
|
if (FALSE == parse_pipe(pcommand, ret_token))
|
|
{
|
|
PIPE_ERROR_INIT();
|
|
SPRINTF(error_str, "%s%s", INVALID_CMD, ret_token);
|
|
rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_STR(error_str));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check the shell device parameter before the fork/exec
|
|
It is not required, but must not be null if entered */
|
|
if (sparams[PSHELL] && (0 == slen[PSHELL]))
|
|
{
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("SHELL parameter has no value"));
|
|
} else if (0 != slen[PSHELL])
|
|
{
|
|
pshell = malloc(slen[PSHELL] + 1);
|
|
memcpy(pshell, sparams[PSHELL], slen[PSHELL]);
|
|
pshell[slen[PSHELL]] = '\0';
|
|
/* The shell must be a regular file and executable */
|
|
STAT_FILE(pshell, &sb, ret_stat);
|
|
if (-1 == ret_stat || !(S_ISREG(sb.st_mode)) || !(sb.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)))
|
|
{
|
|
save_errno = errno;
|
|
assert(GTM_MAX_DIR_LEN - 1 >= STRLEN(pshell));
|
|
SPRINTF(error_str, "Invalid shell: %s", pshell);
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_STR(error_str));
|
|
}
|
|
pshell_name = basename(pshell);
|
|
}
|
|
|
|
/* check the stderr device parameter before the fork/exec
|
|
It is not required, but must not be null if entered */
|
|
if (sparams[PSTDERR] && (0 == slen[PSTDERR]))
|
|
{
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(8) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("STDERR parameter has no value"));
|
|
} else if (0 != slen[PSTDERR])
|
|
{
|
|
pstderr = malloc(slen[PSTDERR] + 1);
|
|
memcpy(pstderr, sparams[PSTDERR], slen[PSTDERR]);
|
|
pstderr[slen[PSTDERR]] = '\0';
|
|
return_stderr = TRUE;
|
|
}
|
|
|
|
/* child to read from pfd_write[0] and parent to write to pfd_write[1] */
|
|
if (-1 == pipe(pfd_write))
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("PIPE - pipe(pfd_write) failed"), save_errno);
|
|
} else
|
|
{
|
|
#ifdef __MVS__
|
|
PIPE_CVT_FROM_CHSET(write_chset, ccsid, textflag, write_cvt);
|
|
/* only default and binary get tagged */
|
|
if (-1 == gtm_zos_set_tag(pfd_write[0], ccsid, textflag, TAG_FORCE, &realfiletag))
|
|
TAG_POLICY_SEND_MSG("PIPE - ccsid tag(pfd_write) failed", errno, realfiletag, ccsid);
|
|
#endif
|
|
iod->dollar.zeof = FALSE;
|
|
}
|
|
|
|
/* child to write stdout (and possibly stderr) to pfd_read[1] and parent to read from pfd_read[0] */
|
|
if (return_stdout)
|
|
if (-1 == pipe(pfd_read))
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("PIPE - pipe(pfd_read) failed"), save_errno);
|
|
} else
|
|
{
|
|
#ifdef __MVS__
|
|
PIPE_CVT_FROM_CHSET(read_chset, ccsid, textflag, read_cvt);
|
|
if (-1 == gtm_zos_set_tag(pfd_read[0], ccsid, textflag, TAG_FORCE, &realfiletag))
|
|
TAG_POLICY_SEND_MSG("PIPE - ccsid tag(pfd_read) failed", errno, realfiletag, ccsid);
|
|
#endif
|
|
file_des_read = pfd_read[0];
|
|
}
|
|
|
|
/* child to write to pfd_read_stderr[1] and parent to read from pfd_read_stderr[0] */
|
|
if (return_stderr)
|
|
if (-1 == pipe(pfd_read_stderr))
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("PIPE - pipe(pfd_read_stderr) failed"), save_errno);
|
|
} else
|
|
{
|
|
#ifdef __MVS__
|
|
/* stderr is always tagged EBCDIC. there are no Unicode or untagged ASCII error messages */
|
|
PIPE_CVT_FROM_CHSET(stderr_chset, ccsid, textflag, dummy_cvt);
|
|
if (-1 == gtm_zos_set_tag(pfd_read_stderr[0], ccsid, textflag, TAG_FORCE, &realfiletag))
|
|
TAG_POLICY_SEND_MSG("PIPE - ccsid tag(pfd_read_stderr) failed", errno, realfiletag, ccsid);
|
|
#endif
|
|
file_des_read_stderr = pfd_read_stderr[0];
|
|
}
|
|
|
|
file_des_write = pfd_write[1];
|
|
/*do the fork and exec */
|
|
cpid = fork(); /* BYPASSOK: we exec() immediately, no FORK_CLEAN needed */
|
|
if (-1 == cpid)
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("PIPE - fork() failed"), save_errno); /* BYPASSOK */
|
|
}
|
|
if (0 == cpid)
|
|
{
|
|
/* in child */
|
|
int ret;
|
|
|
|
CLOSEFILE_RESET(pfd_write[1], rc); /* Close unused write end; Resets "pfd_write[1]" to FD_INVALID */
|
|
ZOS_ONLY(if (-1 == gtm_zos_setcvtmode(pfd_write[0], write_cvt[CHILDCVT]))
|
|
TAG_POLICY_SEND_MSG("PIPE - conversion mode(pfd_write) failed in child", errno, realfiletag, ccsid));
|
|
if (return_stdout)
|
|
{
|
|
CLOSEFILE_RESET(pfd_read[0], rc); /* Close unused read end for stdout return; Reset fd to FD_INVALID */
|
|
ZOS_ONLY(if (-1 == gtm_zos_setcvtmode(pfd_read[1], read_cvt[CHILDCVT]))
|
|
TAG_POLICY_SEND_MSG("PIPE - conversion mode(pfd_read) failed in child", errno, realfiletag, ccsid));
|
|
}
|
|
if (return_stderr)
|
|
CLOSEFILE_RESET(pfd_read_stderr[0], rc); /* Close unused read end for stderr return; Set fd to FD_INVALID */
|
|
/* need to let iorm_close() know not to reap any pipe devices during io_rundown. They should only
|
|
be reaped by the parent process. */
|
|
gtm_pipe_child = TRUE;
|
|
|
|
/* rundown io devices in child */
|
|
io_rundown(RUNDOWN_EXCEPT_STD);
|
|
|
|
/* do common cleanup in child */
|
|
ojchildioclean();
|
|
|
|
CLOSEFILE(0, rc);
|
|
/* stdin becomes pfd_write[0] */
|
|
if (-1 == dup2(pfd_write[0], 0))
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("PIPE - dup2(pfd_write[0]) failed in child"), save_errno);
|
|
}
|
|
if (return_stdout)
|
|
{
|
|
/* stdout becomes pfd_read[1] */
|
|
CLOSEFILE(1, rc);
|
|
if (-1 == dup2(pfd_read[1], 1))
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("PIPE - dup2(pfd_read[1],1) failed in child"), save_errno);
|
|
}
|
|
/* stderr also becomes pfd_read[1] if return_stderr is false*/
|
|
if (FALSE == return_stderr)
|
|
{
|
|
CLOSEFILE(2, rc);
|
|
if (-1 == dup2(pfd_read[1], 2))
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT,
|
|
2, LEN_AND_LIT("PIPE - dup2(pfd_read[1],2) failed in child"), save_errno);
|
|
}
|
|
}
|
|
}
|
|
if (return_stderr)
|
|
{
|
|
CLOSEFILE(2, rc);
|
|
if (-1 == dup2(pfd_read_stderr[1], 2))
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT,
|
|
2, LEN_AND_LIT("PIPE - dup2(pfd_read_stderr[1],2) failed in child"), save_errno);
|
|
}
|
|
}
|
|
if (0 == slen[PSHELL])
|
|
{
|
|
/* get SHELL environment */
|
|
sh = GETENV("SHELL");
|
|
/* use bourne shell as default if SHELL not set in environment*/
|
|
if (!sh)
|
|
{
|
|
ret = EXECL("/bin/sh", "sh", "-c", pcommand, (char *)NULL);
|
|
} else
|
|
ret = EXECL(sh, basename(sh), "-c", pcommand, (char *)NULL);
|
|
} else
|
|
ret = EXECL(pshell, pshell_name, "-c", pcommand, (char *)NULL);
|
|
if (-1 == ret)
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("PIPE - execl() failed in child"), save_errno);
|
|
}
|
|
} else
|
|
{ /* in parent */
|
|
CLOSEFILE_RESET(pfd_write[0], rc); /* Close unused read end; Resets "pfd_write[0]" to FD_INVALID */
|
|
ZOS_ONLY(if (-1 == gtm_zos_setcvtmode(pfd_write[1], write_cvt[PARENTCVT]))
|
|
TAG_POLICY_SEND_MSG("PIPE - conversion mode(pfd_write) failed", errno, realfiletag, ccsid));
|
|
/* if returning stdout then close unused write end */
|
|
if (return_stdout)
|
|
{
|
|
CLOSEFILE_RESET(pfd_read[1], rc); /* resets "pfd_read[1]" to FD_INVALID */
|
|
ZOS_ONLY(if(-1 == gtm_zos_setcvtmode(pfd_read[0], read_cvt[PARENTCVT]))
|
|
TAG_POLICY_SEND_MSG("PIPE - conversion mode(pfd_read) failed", errno, realfiletag, ccsid));
|
|
}
|
|
if (return_stderr)
|
|
CLOSEFILE_RESET(pfd_read_stderr[1], rc); /* resets "pfd_read_stderr[1]" to FD_INVALID */
|
|
}
|
|
assert((params) *(pp->str.addr) < (unsigned char) n_iops);
|
|
assert(0 != iod);
|
|
assert(0 <= iod->state && iod->state < n_io_dev_states);
|
|
assert(pi == iod->type);
|
|
if (!(d_rm = (d_rm_struct *) iod->dev_sp))
|
|
{ iod->dev_sp = (void*)malloc(SIZEOF(d_rm_struct));
|
|
memset(iod->dev_sp, 0, SIZEOF(d_rm_struct));
|
|
d_rm = (d_rm_struct *) iod->dev_sp;
|
|
/* save child's process id for reaping on close */
|
|
d_rm->pipe_pid = cpid;
|
|
/* save read file descriptor for stdout return if set */
|
|
if (file_des_read)
|
|
{
|
|
if (NULL == (d_rm->read_filstr = FDOPEN(file_des_read, "r")))
|
|
{
|
|
save_errno = errno;
|
|
PIPE_ERROR_INIT();
|
|
rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
|
|
ERR_TEXT, 2, LEN_AND_LIT("Error in stream open"), save_errno);
|
|
}
|
|
d_rm->read_fildes = file_des_read;
|
|
}
|
|
SPRINTF(&d_rm->dollar_key[0], "%d", cpid); /* save in pipe specific structure for $KEY access */
|
|
memcpy(d_rm->dollar_device, "0", SIZEOF("0"));
|
|
iod->state = dev_closed;
|
|
d_rm->stream = FALSE;
|
|
iod->width = DEF_RM_WIDTH;
|
|
iod->length = DEF_RM_LENGTH;
|
|
d_rm->independent = independent;
|
|
d_rm->parse = parse;
|
|
d_rm->recordsize = DEF_RM_RECORDSIZE;
|
|
d_rm->def_width = d_rm->def_recsize = TRUE;
|
|
d_rm->fixed = FALSE;
|
|
d_rm->noread = FALSE;
|
|
d_rm->padchar = DEF_RM_PADCHAR;
|
|
d_rm->inbuf = NULL;
|
|
d_rm->outbuf = NULL;
|
|
d_rm->dev_param_pairs.num_pairs = param_cnt;
|
|
if (slen[PSHELL])
|
|
{
|
|
MALLOC_CPY_LIT(d_rm->dev_param_pairs.pairs[param_offset].name, "SHELL=");
|
|
/* add quotes around the command field */
|
|
d_rm->dev_param_pairs.pairs[param_offset].definition = malloc(STRLEN(pshell) + 5);
|
|
SPRINTF(d_rm->dev_param_pairs.pairs[param_offset++].definition, "\"%s\"", pshell);
|
|
if (NULL != pshell)
|
|
free(pshell);
|
|
}
|
|
/* We checked for command existence earlier so no need to check again */
|
|
MALLOC_CPY_LIT(d_rm->dev_param_pairs.pairs[param_offset].name, "COMMAND=");
|
|
/* add quotes around the command field */
|
|
d_rm->dev_param_pairs.pairs[param_offset].definition = malloc(STRLEN(pcommand) + 5);
|
|
SPRINTF(d_rm->dev_param_pairs.pairs[param_offset++].definition, "\"%s\"", pcommand);
|
|
if (NULL != pcommand)
|
|
free(pcommand);
|
|
if (slen[PSTDERR])
|
|
{
|
|
MALLOC_CPY_LIT(d_rm->dev_param_pairs.pairs[param_offset].name, "STDERR=");
|
|
/* add quotes around the stderr field */
|
|
d_rm->dev_param_pairs.pairs[param_offset].definition = malloc(STRLEN(pstderr) + 5);
|
|
SPRINTF(d_rm->dev_param_pairs.pairs[param_offset].definition, "\"%s\"", pstderr);
|
|
if (NULL != pstderr)
|
|
free(pstderr);
|
|
}
|
|
}
|
|
d_rm->pipe = TRUE;
|
|
iod->type = rm;
|
|
|
|
if (return_stderr)
|
|
{
|
|
/* We will create an mstr (using MSTR_DEF in mdef.h) and call get_log_name(..,INSERT) like
|
|
op_open does to create and insert io_log_name * in io_root_log_name. */
|
|
|
|
|
|
MSTR_DEF(stderr_mstr, slen[PSTDERR], sparams[PSTDERR]);
|
|
stderr_naml = get_log_name(&stderr_mstr, INSERT);
|
|
|
|
stderr_iod = stderr_naml->iod = (io_desc *)malloc(SIZEOF(io_desc));
|
|
memset((char*)stderr_naml->iod, 0, SIZEOF(io_desc));
|
|
stderr_naml->iod->pair.in = stderr_naml->iod;
|
|
stderr_naml->iod->pair.out = stderr_naml->iod;
|
|
stderr_naml->iod->trans_name = stderr_naml;
|
|
(stderr_iod->pair.in)->trans_name = stderr_naml;
|
|
{
|
|
io_desc *io_ptr;
|
|
d_rm_struct *in_d_rm;
|
|
|
|
io_ptr = ( stderr_iod->pair.in);
|
|
if (!(in_d_rm = (d_rm_struct *) io_ptr->dev_sp))
|
|
{
|
|
io_ptr->dev_sp = (void*)malloc(SIZEOF(d_rm_struct));
|
|
memset(io_ptr->dev_sp, 0, SIZEOF(d_rm_struct));
|
|
in_d_rm = (d_rm_struct *) io_ptr->dev_sp;
|
|
memcpy(in_d_rm->dollar_device, "0", SIZEOF("0"));
|
|
io_ptr->state = dev_closed;
|
|
in_d_rm->stream = d_rm->stream;
|
|
io_ptr->width = iod->width;
|
|
io_ptr->length = iod->length;
|
|
io_ptr->wrap = DEFAULT_IOD_WRAP;
|
|
io_ptr->dollar.x = 0;
|
|
io_ptr->dollar.y = 0;
|
|
io_ptr->dollar.za = 0;
|
|
io_ptr->dollar.zb[0] = 0;
|
|
io_ptr->disp_ptr = iod->disp_ptr;
|
|
in_d_rm->fixed = d_rm->fixed;
|
|
in_d_rm->noread = TRUE;
|
|
in_d_rm->padchar = d_rm->padchar;
|
|
in_d_rm->inbuf = d_rm->inbuf;
|
|
in_d_rm->outbuf = d_rm->outbuf;
|
|
in_d_rm->stderr_parent = iod;
|
|
in_d_rm->read_fildes = FD_INVALID; /* checked in iorm_get_bom to get correct file descriptor */
|
|
}
|
|
in_d_rm->pipe = TRUE;
|
|
io_ptr->type = rm;
|
|
status_read = iorm_open(stderr_naml, pp, file_des_read_stderr, mspace, timeout);
|
|
if (TRUE == status_read)
|
|
( stderr_iod->pair.in)->state = dev_open;
|
|
else if (dev_open == ( stderr_iod->pair.out)->state)
|
|
( stderr_iod->pair.in)->state = dev_closed;
|
|
}
|
|
d_rm->stderr_child = stderr_iod;
|
|
}
|
|
flags = 0;
|
|
FCNTL2(file_des_write, F_GETFL, flags);
|
|
if (0 > flags)
|
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno);
|
|
FCNTL3(file_des_write, F_SETFL, (flags | O_NONBLOCK), fcntl_res);
|
|
if (0 > fcntl_res)
|
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno);
|
|
return iorm_open(dev_name, pp, file_des_write, mspace, timeout);
|
|
}
|