/**************************************************************** * * * 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 #include #include #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->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); }