2012-02-05 11:35:58 -05:00
|
|
|
/****************************************************************
|
|
|
|
* *
|
2012-03-24 14:06:46 -04:00
|
|
|
* Copyright 2001, 2012 Fidelity Information Services, Inc *
|
2012-02-05 11:35:58 -05:00
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* -------------------------------------------------------
|
|
|
|
* This routine starts a new child process and passes the
|
|
|
|
* job parameters to it.
|
|
|
|
* -------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "mdef.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include "gtm_fcntl.h"
|
|
|
|
#include "gtm_stdlib.h"
|
|
|
|
#include "gtm_unistd.h"
|
|
|
|
#include "gtm_stdio.h"
|
|
|
|
#include "gtm_stat.h"
|
|
|
|
#include "gtm_string.h"
|
|
|
|
#if defined(SYS_ERRLIST_INCLUDE) && !defined(__CYGWIN__)
|
|
|
|
#include SYS_ERRLIST_INCLUDE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "job.h"
|
|
|
|
#include "error.h"
|
|
|
|
#include "rtnhdr.h"
|
|
|
|
#include "io.h"
|
|
|
|
#include "iosp.h"
|
|
|
|
#include "eintr_wrappers.h"
|
|
|
|
#include "compiler.h"
|
|
|
|
#include "job_addr.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "gtm_facility.h"
|
|
|
|
#include "fileinfo.h"
|
|
|
|
#include "gtmio.h"
|
|
|
|
|
|
|
|
#define MAX_JOB_LEN 8192 /* Arbitrary length maximum used for checking job arguments and parameters */
|
|
|
|
#define MAX_PATH 128 /* Maximum file path length */
|
|
|
|
#define MAX_LAB_LEN 32 /* Maximum Label string length */
|
|
|
|
#define MAX_RTN_LEN 32 /* Maximum Routine string length */
|
|
|
|
#define TEMP_BUFF_SIZE 1024
|
|
|
|
#define PARM_STRING_SIZE 9
|
|
|
|
#define MAX_NUM_LEN 10 /* Maximum length number will be when converted to string */
|
|
|
|
#define MAX_JOB_QUALS 12 /* Maximum environ variables set for job qualifiers */
|
|
|
|
#define MUMPS_EXE_STR "/mumps"
|
|
|
|
#define MUMPS_DIRECT_STR "-direct"
|
|
|
|
#define GTMJ_FMT "gtmj%03d="
|
|
|
|
#define PARM_STR "gtmj000="
|
|
|
|
|
|
|
|
static int joberr = joberr_gen;
|
|
|
|
static boolean_t job_launched = FALSE;
|
|
|
|
|
|
|
|
GBLREF bool jobpid; /* job's output files should have the pid appended to them. */
|
|
|
|
GBLREF volatile boolean_t ojtimeout;
|
|
|
|
GBLREF boolean_t job_try_again;
|
|
|
|
GBLREF uint4 process_id;
|
|
|
|
GBLREF boolean_t gtm_pipe_child;
|
|
|
|
#ifndef SYS_ERRLIST_INCLUDE
|
|
|
|
/* currently either stdio.h or errno.h both of which are included above */
|
|
|
|
/* needed by TIMEOUT_ERROR in jobsp.h */
|
|
|
|
#if !defined(__sun) && !defined(___MVS__)
|
|
|
|
GBLREF int sys_nerr;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __osf__
|
|
|
|
/* environ is from the O/S which only uses 64-bit pointers on OSF/1. */
|
|
|
|
#pragma pointer_size (save)
|
|
|
|
#pragma pointer_size (long)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
GBLREF char **environ;
|
|
|
|
|
|
|
|
#ifdef __osf__
|
|
|
|
#pragma pointer_size (restore)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
error_def(ERR_JOBFAIL);
|
|
|
|
error_def(ERR_JOBPARTOOLONG);
|
|
|
|
error_def(ERR_LOGTOOLONG);
|
|
|
|
error_def(ERR_TEXT);
|
|
|
|
|
|
|
|
#define KILL_N_REAP(PROCESS_ID, SIGNAL, RET_VAL) \
|
|
|
|
{ \
|
|
|
|
if (-1 != (RET_VAL = kill(PROCESS_ID, SIGNAL))) \
|
|
|
|
{ \
|
|
|
|
/* reap the just killed child, so there wont be any zombies */ \
|
|
|
|
WAITPID(PROCESS_ID, &wait_status, 0, done_pid); \
|
|
|
|
assert(done_pid == PROCESS_ID); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Note that this module uses _exit instead of exit to avoid running the inherited
|
|
|
|
exit handlers which this mid-level process does not want to run */
|
|
|
|
|
|
|
|
/* if we get an error assembling the child environment,
|
|
|
|
we don't want a rogue child running loose */
|
|
|
|
|
|
|
|
void job_term_handler(int sig);
|
|
|
|
|
|
|
|
static CONDITION_HANDLER(bad_child)
|
|
|
|
{
|
|
|
|
PRN_ERROR;
|
|
|
|
_exit(joberr);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define FAILED_TO_LAUNCH 1
|
|
|
|
|
|
|
|
/* This is to close the window of racing condition where the timeout occurs and actually
|
|
|
|
* by that time, the middle process had already successfully forked the job
|
|
|
|
*/
|
|
|
|
|
|
|
|
void job_term_handler(int sig){
|
|
|
|
if (job_launched)
|
|
|
|
_exit(0);
|
|
|
|
else
|
|
|
|
_exit(FAILED_TO_LAUNCH);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ---------------------------------------------------------------------------------------------------------------------
|
|
|
|
* The current process (P) FORKs a middle child process (M) that tests various job parameters. It then forks off the
|
|
|
|
* actual Job (J) and exits, culminating the parent's (P) wait. The Job process (J) sets up its env and exexs mumps.
|
|
|
|
*
|
|
|
|
* Arguments
|
|
|
|
* First argument is a pointer to the structure holding Job parameter values.
|
|
|
|
* Second argument is the number of parameters being passed.
|
|
|
|
* The third boolean argument indicates to the caller if the return from this function was due to an exit from the
|
|
|
|
* middle process or due to reasons other than that. It is set to true for the latter case of return.
|
|
|
|
* Fourth argument is the pair of file descriptors [opened by pipe] for the child process (M) to write PID
|
|
|
|
* of the jobbed off process (J).
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* Exit status of child (that the parent gets by WAITing) in case the return was after an exit from the middle process.
|
|
|
|
* errno in other cases with the third argument set to TRUE and returned by pointer.
|
|
|
|
* TIMEOUT_ERROR in case a timeout occured.
|
|
|
|
* Return zero indicates success.
|
|
|
|
* ---------------------------------------------------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
int ojstartchild (job_params_type *jparms, int argcnt, boolean_t *non_exit_return, int pipe_fds[])
|
|
|
|
{
|
|
|
|
char cbuff[TEMP_BUFF_SIZE], pbuff[TEMP_BUFF_SIZE];
|
|
|
|
char tbuff[MAX_JOB_LEN], tbuff2[MAX_JOB_LEN];
|
|
|
|
char *pgbldir_str;
|
|
|
|
char *transfer_addr;
|
|
|
|
int4 index, environ_count, string_len, temp;
|
|
|
|
int wait_status, save_errno, kill_ret;
|
|
|
|
int rc;
|
|
|
|
bool status;
|
|
|
|
pid_t par_pid, child_pid, done_pid;
|
|
|
|
job_parm *jp;
|
|
|
|
rhdtyp *base_addr;
|
|
|
|
struct sigaction act, old_act;
|
|
|
|
int pipe_status, env_len;
|
|
|
|
|
|
|
|
#ifdef __osf__
|
|
|
|
/* These must be O/S-compatible 64-bit pointers for OSF/1. */
|
|
|
|
#pragma pointer_size (save)
|
|
|
|
#pragma pointer_size (long)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
char *c1, *c2, **c3;
|
|
|
|
char *argv[3];
|
|
|
|
char **env_ary, **env_ind;
|
|
|
|
char **new_env_cur, **new_env_top, **old_env_cur, **old_env_top, *env_end;
|
|
|
|
|
|
|
|
#ifdef __osf__
|
|
|
|
#pragma pointer_size (restore)
|
|
|
|
#endif
|
|
|
|
job_launched = FALSE;
|
|
|
|
par_pid = process_id;
|
2012-03-24 14:06:46 -04:00
|
|
|
if (-1 == (child_pid = fork())) /* BYPASSOK: we die after creating a child, no FORK_CLEAN needed */
|
2012-02-05 11:35:58 -05:00
|
|
|
{
|
|
|
|
if (EAGAIN == errno || ENOMEM == errno)
|
|
|
|
job_try_again = TRUE;
|
|
|
|
*non_exit_return = TRUE;
|
|
|
|
return (errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (child_pid == 0)
|
|
|
|
{
|
|
|
|
/* This is a child process (middle process, M) */
|
|
|
|
/* Test out various parameters and setup everything possible for the actual Job (J), so it(J) can
|
|
|
|
* start off without much hitch. If any error occurs during this, exit with appropriate status so
|
|
|
|
* the waiting parent can diagnose.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* set to TRUE so any child process associated with a pipe device will know it is not the parent in iorm_close() */
|
|
|
|
gtm_pipe_child = TRUE;
|
|
|
|
joberr = joberr_gen;
|
|
|
|
ESTABLISH_RET(bad_child, 0);
|
|
|
|
|
|
|
|
sigemptyset(&act.sa_mask);
|
|
|
|
act.sa_flags = 0;
|
|
|
|
act.sa_handler = job_term_handler;
|
|
|
|
sigaction(SIGTERM, &act, &old_act);
|
|
|
|
|
|
|
|
if (!jobpid) /* if the Job pid need not be appended to the std-in/out/err file names */
|
|
|
|
{
|
|
|
|
joberr = joberr_io;
|
|
|
|
/* attempt to open output files */
|
|
|
|
/* this also redirects stdin/out/err, so any error messages by this process during
|
|
|
|
* the creation of the Job will get redirected */
|
|
|
|
if (!(status = ojchildioset(jparms)))
|
|
|
|
{
|
|
|
|
if (job_try_again)
|
|
|
|
joberr += joberr_tryagain;
|
|
|
|
rts_error(VARLSTCNT(6) ERR_JOBFAIL, 0, ERR_TEXT, 2,
|
|
|
|
LEN_AND_LIT("Failed to set STDIN/OUT/ERR for the job"));
|
|
|
|
}
|
|
|
|
} /* else, all the errors during the creation of Job go to the STDOUT/ERR of the parent process */
|
|
|
|
|
|
|
|
joberr = joberr_cd; /* pass current directory to child */
|
|
|
|
if (jparms->directory.len != 0)
|
|
|
|
{
|
|
|
|
/* If directory is specified, change it */
|
|
|
|
if (jparms->directory.len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
|
|
|
|
strncpy(pbuff, jparms->directory.addr, jparms->directory.len);
|
|
|
|
*(pbuff + jparms->directory.len) = '\0';
|
|
|
|
if (CHDIR(pbuff) != 0)
|
|
|
|
{
|
|
|
|
if (ETIMEDOUT == errno) /* atleast on AIX */
|
|
|
|
joberr += joberr_tryagain;
|
|
|
|
rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2,
|
|
|
|
LEN_AND_LIT("Error changing directory for the Job."),errno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
joberr = joberr_rtn;
|
|
|
|
job_addr(&jparms->routine, &jparms->label, jparms->offset, (char **)&base_addr, &transfer_addr);
|
|
|
|
|
|
|
|
joberr = joberr_syscall;
|
|
|
|
if (-1 == setsid())
|
|
|
|
rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2,
|
|
|
|
LEN_AND_LIT("Error setting session id for the Job."), errno);
|
|
|
|
|
|
|
|
joberr = joberr_frk;
|
2012-03-24 14:06:46 -04:00
|
|
|
/* clone self and exit */
|
|
|
|
if (0 != (child_pid = fork())) /* BYPASSOK: we exec immediately, no FORK_CLEAN needed */
|
2012-02-05 11:35:58 -05:00
|
|
|
{
|
|
|
|
/* This is still the middle process. */
|
|
|
|
if (0 > child_pid)
|
|
|
|
{
|
|
|
|
if (EAGAIN == errno || ENOMEM == errno)
|
|
|
|
joberr += joberr_tryagain;
|
|
|
|
rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2,
|
|
|
|
LEN_AND_LIT("Error forking the Job."), errno);
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
job_launched = TRUE;
|
|
|
|
|
|
|
|
assert(SIZEOF(pid_t) == SIZEOF(child_pid));
|
|
|
|
/* write child_pid into pipe to be read by parent process(P) for $ZJOB */
|
|
|
|
DOWRITERC(pipe_fds[1], &child_pid, SIZEOF(child_pid), pipe_status);
|
|
|
|
if (0 != pipe_status)
|
|
|
|
{
|
|
|
|
rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2,
|
|
|
|
LEN_AND_LIT("Error writing to pipe"), errno);
|
|
|
|
}
|
|
|
|
_exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is now the grandchild process (actual Job process) -- an orphan as soon as the exit(0) above occurs. */
|
|
|
|
/* set up the environment and exec */
|
|
|
|
|
|
|
|
sigaction(SIGTERM, &old_act, 0); /* restore the SIGTERM handler */
|
|
|
|
|
|
|
|
joberr = joberr_io;
|
|
|
|
|
|
|
|
/* Run down any open flat files to reclaim their file descriptors */
|
|
|
|
io_rundown(RUNDOWN_EXCEPT_STD);
|
|
|
|
|
|
|
|
/* release the pipe opened by grand parent (P) */
|
|
|
|
CLOSEFILE_RESET(pipe_fds[0], pipe_status); /* resets "pipe_fds[0]" to FD_INVALID */
|
|
|
|
CLOSEFILE_RESET(pipe_fds[1], pipe_status); /* resets "pipe_fds[1]" to FD_INVALID */
|
|
|
|
|
|
|
|
/* do common cleanup in child */
|
|
|
|
ojchildioclean();
|
|
|
|
|
|
|
|
/* Count the number of environment variables. */
|
|
|
|
for (environ_count = 0, c3 = environ, c2 = *c3; c2; c3++, c2 = *c3)
|
|
|
|
environ_count++;
|
|
|
|
#ifdef __osf__
|
|
|
|
/* Since we're creating an array of pointers for the O/S, make sure SIZEOF(char *) is correct for 64-bit pointers for OSF/1. */
|
|
|
|
#pragma pointer_size (save)
|
|
|
|
#pragma pointer_size (long)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* the environment array passed to the grandchild is constructed by prefixing the job related environment
|
|
|
|
* variables ahead of the current environment (pointed to by the "environ" variable)
|
|
|
|
*
|
|
|
|
* e.g. if the current environment has only two environment variables env1=one and env2=two,
|
|
|
|
* and the job command is as follows
|
|
|
|
* job ^x(1,2):(output="x.mjo":error="x.mje")
|
|
|
|
*
|
|
|
|
* then the environment array passed is as follows
|
|
|
|
* gtmj0= // parent pid
|
|
|
|
* gtmgbldir=mumps.gld // current global directory
|
|
|
|
* gtmjb= // startup parameter to job command
|
|
|
|
* gtmj3=/dev/null // input file parameter to job command
|
|
|
|
* gtmj4=x.mjo // output file parameter to job command
|
|
|
|
* gtmj5=x.mje // error file parameter to job command
|
|
|
|
* gtmj7=x // routine name to job off
|
|
|
|
* gtmj8= // label name to job off
|
|
|
|
* gtmj9=0 // offset to job off
|
|
|
|
* gtmja= // base priority;
|
|
|
|
* gtmj000=1 // parameter 1 to routine ^x
|
|
|
|
* gtmj001=2 // parameter 2 to routine ^x
|
|
|
|
* gtmjcnt=2 // number of parameters to routine ^x
|
|
|
|
* env1=one // old environment
|
|
|
|
* env2=two // old environment
|
|
|
|
*
|
|
|
|
* those parameters that are NULL or 0 are not passed.
|
|
|
|
* each line above is an entry in the environment array.
|
|
|
|
*/
|
|
|
|
|
|
|
|
env_ind = env_ary = (char **)malloc((environ_count + MAX_JOB_QUALS + argcnt + 1)*SIZEOF(char *));
|
|
|
|
|
|
|
|
#ifdef __osf__
|
|
|
|
#pragma pointer_size (restore)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
string_len = STRLEN("%s=%d") + STRLEN(CHILD_FLAG_ENV) + MAX_NUM_LEN - 4;
|
|
|
|
if (string_len > MAX_JOB_LEN)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_NUM(c1, CHILD_FLAG_ENV, par_pid, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
/* Pass all information about the job via shell's environment.
|
|
|
|
* The grandchild will get those variables to obtain the info about the job.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* pass global directory to child */
|
|
|
|
if (jparms->gbldir.len != 0)
|
|
|
|
{
|
|
|
|
if (jparms->gbldir.len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
strncpy(pbuff, jparms->gbldir.addr, jparms->gbldir.len);
|
|
|
|
*(pbuff + jparms->gbldir.len) = '\0';
|
|
|
|
string_len = STRLEN("%s=%s") + STRLEN(GBLDIR_ENV) + STRLEN(pbuff) - 4;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_STR(c1, GBLDIR_ENV, pbuff, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
/* pass startup program to child */
|
|
|
|
if (jparms->startup.len != 0)
|
|
|
|
{
|
|
|
|
if (jparms->startup.len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
strncpy(pbuff, jparms->startup.addr, jparms->startup.len);
|
|
|
|
*(pbuff + jparms->startup.len) = '\0';
|
|
|
|
string_len = STRLEN("%s=%s") + STRLEN(STARTUP_ENV) + STRLEN(pbuff) - 4;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_STR(c1, STARTUP_ENV, pbuff, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
/* pass input file to child */
|
|
|
|
if (jparms->input.len != 0)
|
|
|
|
{
|
|
|
|
if (jparms->input.len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
strncpy(pbuff, jparms->input.addr, jparms->input.len);
|
|
|
|
*(pbuff + jparms->input.len) = '\0';
|
|
|
|
string_len = STRLEN("%s=%s") + STRLEN(IN_FILE_ENV) + STRLEN(pbuff) - 4;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_STR(c1, IN_FILE_ENV, pbuff, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pass output file to child */
|
|
|
|
if (jparms->output.addr != 0)
|
|
|
|
{
|
|
|
|
if (jparms->output.len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
strncpy(pbuff, jparms->output.addr, jparms->output.len);
|
|
|
|
*(pbuff + jparms->output.len) = '\0';
|
|
|
|
if (jobpid)
|
|
|
|
SPRINTF(&pbuff[jparms->output.len], ".%d", getpid());
|
|
|
|
string_len = STRLEN("%s=%s") + STRLEN(OUT_FILE_ENV) + STRLEN(pbuff) - 4;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_STR(c1, OUT_FILE_ENV, pbuff, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pass error file to child */
|
|
|
|
if (jparms->error.len != 0)
|
|
|
|
{
|
|
|
|
if (jparms->error.len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
strncpy(pbuff, jparms->error.addr, jparms->error.len);
|
|
|
|
*(pbuff + jparms->error.len) = '\0';
|
|
|
|
if (jobpid)
|
|
|
|
SPRINTF(&pbuff[jparms->error.len], ".%d", getpid());
|
|
|
|
string_len = STRLEN("%s=%s") + STRLEN(ERR_FILE_ENV) + STRLEN(pbuff) - 4;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_STR(c1, ERR_FILE_ENV, pbuff, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pass routine name to child */
|
|
|
|
if (jparms->routine.len != 0)
|
|
|
|
{
|
|
|
|
if (jparms->routine.len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
strncpy(pbuff, jparms->routine.addr, jparms->routine.len);
|
|
|
|
*(pbuff + jparms->routine.len) = '\0';
|
|
|
|
string_len = STRLEN("%s=%s") + STRLEN(ROUTINE_ENV) + STRLEN(pbuff) - 4;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_STR(c1, ROUTINE_ENV, pbuff, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pass label name to child */
|
|
|
|
if (jparms->label.len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
strncpy(pbuff, jparms->label.addr, jparms->label.len);
|
|
|
|
*(pbuff + jparms->label.len) = '\0';
|
|
|
|
string_len = STRLEN("%s=%s") + STRLEN(LABEL_ENV) + STRLEN(pbuff) - 4;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_STR(c1, LABEL_ENV, pbuff, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* pass the offset */
|
|
|
|
string_len = STRLEN("%s=%ld") + STRLEN(OFFSET_ENV) + MAX_NUM_LEN - 5;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_NUM(c1, OFFSET_ENV, jparms->offset, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* pass Priority to child */
|
|
|
|
if (jparms->baspri != 0)
|
|
|
|
{
|
|
|
|
string_len = STRLEN("%s=%ld") + STRLEN(PRIORITY_ENV) + MAX_NUM_LEN - 5;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_NUM(c1, PRIORITY_ENV, jparms->baspri, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
for (index = 0, jp = jparms->parms; jp ; index++, jp = jp->next)
|
|
|
|
{
|
|
|
|
if (jp->parm->str.len > MAX_JOB_LEN - 2)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
if (0 != jp->parm->mvtype)
|
|
|
|
{
|
|
|
|
MV_FORCE_STR(jp->parm);
|
|
|
|
string_len = STRLEN(PARM_STR) + jp->parm->str.len + 1;
|
|
|
|
if (string_len > MAX_JOB_LEN)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len);
|
|
|
|
# ifdef KEEP_zOS_EBCDIC
|
|
|
|
__getEstring1_a_copy(c1, STR_AND_LEN(PARM_STRING));
|
|
|
|
__getEstring1_a_copy(c1 + strlen(PARM_STRING), jp->parm->str.addr, jp->parm->str.len);
|
|
|
|
# else
|
|
|
|
SPRINTF(c1, GTMJ_FMT, index);
|
|
|
|
memcpy(c1 + strlen(PARM_STR), jp->parm->str.addr, jp->parm->str.len);
|
|
|
|
# endif
|
|
|
|
*(c1 + string_len - 1) = 0;
|
|
|
|
*env_ind++ = c1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
string_len = STRLEN("%s=%ld") + STRLEN(GTMJCNT_ENV) + MAX_NUM_LEN - 5;
|
|
|
|
if (string_len > TEMP_BUFF_SIZE)
|
|
|
|
rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG);
|
|
|
|
c1 = (char *)malloc(string_len + 1);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(suspend)
|
|
|
|
#endif
|
|
|
|
SPRINTF_ENV_NUM(c1, GTMJCNT_ENV, index, env_ind);
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
#pragma convlit(resume)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __osf__
|
|
|
|
/* Make sure SIZEOF(char *) is correct. */
|
|
|
|
#pragma pointer_size (save)
|
|
|
|
#pragma pointer_size (long)
|
|
|
|
#endif
|
|
|
|
/* before appending the old environment into the environment array, do not add those
|
|
|
|
* lines that correspond to any of the above already initialized environment variables.
|
|
|
|
* this prevents indefinite growing of the environment array with nesting of job commands
|
|
|
|
* which otherwise would show up eventually as an "Arg list too long" error from EXECVE() below.
|
|
|
|
*/
|
|
|
|
new_env_top = env_ind;
|
|
|
|
old_env_top = &environ[environ_count];
|
|
|
|
DEBUG_ONLY(
|
|
|
|
/* check that all new environment variables begin with the string "gtm".
|
|
|
|
* this assumption is used later in the for loop below.
|
|
|
|
*/
|
|
|
|
for (new_env_cur = env_ary; new_env_cur < new_env_top; new_env_cur++)
|
|
|
|
assert(!STRNCMP_LIT(*new_env_cur, "gtm"));
|
|
|
|
)
|
|
|
|
for (old_env_cur = environ; old_env_cur < old_env_top; old_env_cur++)
|
|
|
|
{
|
|
|
|
env_end = strchr(*old_env_cur, '=');
|
|
|
|
if ((NULL != env_end) && !STRNCMP_LIT(*old_env_cur, "gtm"))
|
|
|
|
{
|
|
|
|
env_len = (int)(env_end - *old_env_cur + 1); /* include the '=' too */
|
|
|
|
assert(env_len <= strlen(*old_env_cur));
|
|
|
|
for (new_env_cur = env_ary; new_env_cur < new_env_top; new_env_cur++)
|
|
|
|
{
|
|
|
|
if (0 == strncmp(*new_env_cur, *old_env_cur, env_len))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (new_env_cur < new_env_top)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*env_ind++ = *old_env_cur;
|
|
|
|
}
|
|
|
|
*env_ind = NULL; /* null terminator required by execve() */
|
|
|
|
|
|
|
|
#ifdef __osf__
|
|
|
|
#pragma pointer_size (restore)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
c1 = GETENV("gtm_dist");
|
|
|
|
string_len = STRLEN(c1);
|
|
|
|
if ((string_len + SIZEOF(MUMPS_EXE_STR)) < SIZEOF(tbuff))
|
|
|
|
{
|
|
|
|
memcpy(tbuff, c1, string_len);
|
|
|
|
c2 = &tbuff[string_len];
|
|
|
|
strcpy(c2, MUMPS_EXE_STR);
|
|
|
|
} else
|
|
|
|
rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, string_len, c1, SIZEOF(tbuff) - SIZEOF(MUMPS_EXE_STR));
|
|
|
|
|
|
|
|
# ifdef KEEP_zOS_EBCDIC_ /* use real strcpy to preserve env in native code set */
|
|
|
|
# pragma convlit(suspend)
|
|
|
|
# endif
|
|
|
|
strcpy(cbuff, MUMPS_DIRECT_STR);
|
|
|
|
# ifdef KEEP_zOS_EBCDIC_
|
|
|
|
# pragma convlit(resume)
|
|
|
|
# endif
|
|
|
|
|
|
|
|
#ifdef KEEP_zOS_EBCDIC
|
|
|
|
__getEstring1_a_copy(tbuff2, STR_AND_LEN(tbuff));
|
|
|
|
argv[0] = tbuff2;
|
|
|
|
#else
|
|
|
|
argv[0] = tbuff;
|
|
|
|
#endif
|
|
|
|
argv[1] = cbuff;
|
|
|
|
argv[2] = (char *)0;
|
|
|
|
|
|
|
|
EXECVE(tbuff, argv, env_ary);
|
|
|
|
/* if we got here, error starting the Job */
|
|
|
|
rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Exec error in Job"), errno);
|
|
|
|
REVERT;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
/* Parent, wait for the Middle process */
|
|
|
|
/* Wait ends under the following conditions
|
|
|
|
* The middle process successfully forks off the actual Job or exits with error before doing that.
|
|
|
|
* Timeout for the Job command.
|
|
|
|
* For the first case, return the wait status as got from wait() syscall.
|
|
|
|
* For the second condition, set non_exit_return to TRUE and return the relevant errno.
|
|
|
|
*/
|
|
|
|
|
|
|
|
do
|
|
|
|
{ /* note : macro, WAITPID would not be suitable here because we want to catch our timeout
|
|
|
|
* waitpid expects an integer wait_status even for _BSD cases, but WIF* macros expect
|
|
|
|
* a union wait argument (on AIX)
|
|
|
|
*/
|
|
|
|
done_pid = waitpid(child_pid, &wait_status, 0); /* BYPASSOK */
|
|
|
|
} while(!ojtimeout && 0 > done_pid && EINTR == errno);
|
|
|
|
if (done_pid == child_pid)
|
|
|
|
return (wait_status);
|
|
|
|
else if (0 > done_pid && EINTR == errno && TRUE == ojtimeout)
|
|
|
|
{
|
|
|
|
/* Kill the middle process with SIGTERM and check the exit status from
|
|
|
|
* the handler to see if the Middle process had actually successfully forked the Job */
|
|
|
|
KILL_N_REAP(child_pid, SIGTERM, kill_ret);
|
|
|
|
if (-1 == kill_ret && ESRCH == errno) /* if the middle process finished by now */
|
|
|
|
{
|
|
|
|
WAITPID(child_pid, &wait_status, 0, done_pid);
|
|
|
|
if (done_pid == child_pid)
|
|
|
|
return (wait_status);
|
|
|
|
} else if (-1 != kill_ret && done_pid == child_pid && 0 == wait_status)
|
|
|
|
return 0; /* timer popped in the window of child fork and middle process exit */
|
|
|
|
*non_exit_return = TRUE;
|
|
|
|
return TIMEOUT_ERROR; /* return special value so as to eliminate the window where the timer
|
|
|
|
* might pop after this routine returns to the callee and before the callee
|
|
|
|
* analyses the return status (ojtimeout may not be a reliable indicator) */
|
|
|
|
} else if (0 > done_pid)
|
|
|
|
{
|
|
|
|
*non_exit_return = TRUE;
|
|
|
|
save_errno = errno;
|
|
|
|
KILL_N_REAP(child_pid, SIGKILL, kill_ret);
|
|
|
|
return (save_errno);
|
|
|
|
} else if (0 == done_pid) /* this case should never arise */
|
|
|
|
{
|
|
|
|
assert(FALSE);
|
|
|
|
*non_exit_return = TRUE;
|
|
|
|
KILL_N_REAP(child_pid, SIGKILL, kill_ret);
|
|
|
|
return (EINVAL); /* return some error condition */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (EINVAL); /* This should never get executed, added to make compiler happy */
|
|
|
|
}
|