263 lines
7.4 KiB
C
263 lines
7.4 KiB
C
|
/****************************************************************
|
||
|
* *
|
||
|
* Copyright 2001, 2010 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. *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
#include "mdef.h"
|
||
|
|
||
|
#include <stdarg.h>
|
||
|
#include "gtm_stdio.h"
|
||
|
|
||
|
#include <sys/wait.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include "gtm_string.h"
|
||
|
#include "gtm_unistd.h"
|
||
|
#include "gtmio.h"
|
||
|
#include "eintr_wrappers.h"
|
||
|
#include "iotimer.h"
|
||
|
#include "job.h"
|
||
|
#include "joberr.h"
|
||
|
#include "gt_timer.h"
|
||
|
#include "util.h"
|
||
|
#include "outofband.h"
|
||
|
#include "op.h"
|
||
|
#include "io.h"
|
||
|
#include "mvalconv.h"
|
||
|
|
||
|
GBLDEF short jobcnt = 0;
|
||
|
GBLDEF volatile boolean_t ojtimeout = TRUE;
|
||
|
|
||
|
GBLREF uint4 dollar_zjob;
|
||
|
GBLREF int4 outofband;
|
||
|
GBLREF int dollar_truth;
|
||
|
GBLREF boolean_t job_try_again;
|
||
|
|
||
|
static int4 tid; /* Job Timer ID */
|
||
|
void job_timer_handler(void);
|
||
|
|
||
|
#define MAX_CHAR_CAPACITY 0xFF
|
||
|
|
||
|
/*
|
||
|
* ---------------------------------------------------
|
||
|
* This handler is executed if job could not be started
|
||
|
* during the specified timeout period
|
||
|
* ---------------------------------------------------
|
||
|
*/
|
||
|
void job_timer_handler(void)
|
||
|
{
|
||
|
ojtimeout = TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* ---------------------------------------------------
|
||
|
* Job command main entry point
|
||
|
* ---------------------------------------------------
|
||
|
*/
|
||
|
int op_job(int4 argcnt, ...)
|
||
|
{
|
||
|
va_list var;
|
||
|
int4 i;
|
||
|
mval *label, *inp;
|
||
|
int4 offset;
|
||
|
mval *routine, *param_buf;
|
||
|
int4 timeout; /* timeout in seconds */
|
||
|
int4 msec_timeout; /* timeout in milliseconds */
|
||
|
boolean_t timed, single_attempt, non_exit_return;
|
||
|
unsigned char buff[128], *c;
|
||
|
int4 status, exit_stat, term_sig, stop_sig;
|
||
|
pid_t zjob_pid = 0; /* zjob_pid should exactly match in type with child_pid(ojstartchild.c) */
|
||
|
int pipe_fds[2], pipe_status;
|
||
|
#ifdef _BSD
|
||
|
union wait wait_stat;
|
||
|
#else
|
||
|
int4 wait_stat;
|
||
|
#endif
|
||
|
|
||
|
job_params_type job_params;
|
||
|
char combuf[128];
|
||
|
mstr command;
|
||
|
job_parm *jp;
|
||
|
|
||
|
error_def(ERR_TEXT);
|
||
|
error_def(ERR_JOBFAIL);
|
||
|
|
||
|
VAR_START(var, argcnt);
|
||
|
assert(argcnt >= 5);
|
||
|
label = va_arg(var, mval *);
|
||
|
offset = va_arg(var, int4);
|
||
|
routine = va_arg(var, mval *);
|
||
|
param_buf = va_arg(var, mval *);
|
||
|
timeout = va_arg(var, int4); /* in seconds */
|
||
|
argcnt -= 5;
|
||
|
|
||
|
/* initialize $zjob = 0, in case JOB fails */
|
||
|
dollar_zjob = 0;
|
||
|
|
||
|
MV_FORCE_DEFINED(label);
|
||
|
MV_FORCE_DEFINED(routine);
|
||
|
MV_FORCE_DEFINED(param_buf);
|
||
|
|
||
|
/* create a pipe to channel the PID of the jobbed off process(J) from middle level
|
||
|
* process(M) to the current process (P) */
|
||
|
OPEN_PIPE(pipe_fds, pipe_status);
|
||
|
if (-1 == pipe_status)
|
||
|
{
|
||
|
va_end(var);
|
||
|
rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Error creating pipe"), errno);
|
||
|
}
|
||
|
jobcnt++;
|
||
|
command.addr = &combuf[0];
|
||
|
|
||
|
/* Setup job parameters by parsing param_buf and using label, offset, routine, & timeout). */
|
||
|
job_params.routine = routine->str;
|
||
|
job_params.label = label->str;
|
||
|
job_params.offset = offset;
|
||
|
ojparams(param_buf->str.addr, &job_params);
|
||
|
|
||
|
/* Clear the buffers */
|
||
|
flush_pio();
|
||
|
|
||
|
/* Start the timer */
|
||
|
ojtimeout = FALSE;
|
||
|
single_attempt = FALSE;
|
||
|
if (NO_M_TIMEOUT == timeout)
|
||
|
{
|
||
|
timed = FALSE;
|
||
|
msec_timeout = NO_M_TIMEOUT;
|
||
|
} else
|
||
|
{
|
||
|
timed = TRUE;
|
||
|
msec_timeout = timeout2msec(timeout);
|
||
|
if (msec_timeout > 0)
|
||
|
start_timer((TID)&tid, msec_timeout, job_timer_handler, 0, NULL);
|
||
|
else
|
||
|
single_attempt = TRUE;
|
||
|
}
|
||
|
|
||
|
if (argcnt)
|
||
|
{
|
||
|
jp = job_params.parms = (job_parm *)malloc(SIZEOF(job_parm) * argcnt);
|
||
|
i = argcnt;
|
||
|
for(;;)
|
||
|
{
|
||
|
inp = va_arg(var, mval *);
|
||
|
jp->parm = inp;
|
||
|
if (0 == --i)
|
||
|
break;
|
||
|
jp->next = jp + 1;
|
||
|
jp = jp->next;
|
||
|
}
|
||
|
jp->next = 0;
|
||
|
} else
|
||
|
job_params.parms = 0;
|
||
|
va_end(var);
|
||
|
|
||
|
assert(joberr_tryagain + 1 == joberr_end); /* they must be adjacent and the last two */
|
||
|
assert((joberr_tryagain * 2 - 1) < MAX_CHAR_CAPACITY);
|
||
|
/* Setup parameters and start the job */
|
||
|
do
|
||
|
{
|
||
|
job_try_again = FALSE;
|
||
|
non_exit_return = FALSE;
|
||
|
status = ojstartchild(&job_params, argcnt, &non_exit_return, pipe_fds);
|
||
|
if (status && !non_exit_return)
|
||
|
{
|
||
|
/* check if it was a try_again kind of failure */
|
||
|
#ifdef _BSD
|
||
|
assert(SIZEOF(wait_stat) == SIZEOF(int4));
|
||
|
wait_stat.w_status = status;
|
||
|
/* waitpid() in ojstartchild() expects an int wait_status whereas the WIF* macros expect a
|
||
|
* union wait_stat as an arg */
|
||
|
#else
|
||
|
wait_stat = status;
|
||
|
#endif
|
||
|
if (WIFEXITED(wait_stat) && (joberr_tryagain < (exit_stat = WEXITSTATUS(wait_stat))))
|
||
|
{
|
||
|
/* one of try-again situations */
|
||
|
job_try_again = TRUE;
|
||
|
exit_stat -= joberr_tryagain;
|
||
|
assert(exit_stat < joberr_stp);
|
||
|
}
|
||
|
}
|
||
|
} while (!single_attempt && status && !ojtimeout && job_try_again);
|
||
|
|
||
|
if (argcnt)
|
||
|
free(job_params.parms);
|
||
|
if (timed && !ojtimeout)
|
||
|
cancel_timer((TID)&tid);
|
||
|
|
||
|
/* the child process (M), that wrote to pipe, would have been exited by now */
|
||
|
CLOSEFILE_RESET(pipe_fds[1], pipe_status); /* close the write-end to make the following read non-blocking;
|
||
|
* also resets "pipe_fds[1]" to FD_INVALID */
|
||
|
assert(SIZEOF(pid_t) == SIZEOF(zjob_pid));
|
||
|
DOREADRC(pipe_fds[0], &zjob_pid, SIZEOF(zjob_pid), pipe_status); /* read jobbed off PID from pipe */
|
||
|
if (0 < pipe_status) /* empty pipe (pipe_status == -1) is ignored and not reported as error */
|
||
|
rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Error reading from pipe"), errno);
|
||
|
CLOSEFILE_RESET(pipe_fds[0], pipe_status); /* release the pipe; also resets "pipe_fds[0]" to FD_INVALID */
|
||
|
if (status)
|
||
|
{
|
||
|
if (timed) /* $test should be modified only for timed job commands */
|
||
|
dollar_truth = 0;
|
||
|
if (non_exit_return)
|
||
|
{
|
||
|
if (TIMEOUT_ERROR != status) /* one of errno returns, not the wait_status/timeout situation */
|
||
|
rts_error(VARLSTCNT(3) ERR_JOBFAIL, 0, status);
|
||
|
else
|
||
|
return FALSE;
|
||
|
} else /* wait_status from the child */
|
||
|
{
|
||
|
if (WIFSIGNALED(wait_stat)) /* child was SIGNALed and TERMINated */
|
||
|
{
|
||
|
term_sig = WTERMSIG(wait_stat); /* signal that caused the termination */
|
||
|
memcpy(buff, joberrs[joberr_sig].msg, joberrs[joberr_sig].len);
|
||
|
c = i2asc(&buff[joberrs[joberr_sig].len], term_sig);
|
||
|
rts_error(VARLSTCNT(6) ERR_JOBFAIL, 0, ERR_TEXT, 2, c - buff, buff);
|
||
|
} else if (WIFSTOPPED(wait_stat)) /* child was STOPped */
|
||
|
{
|
||
|
stop_sig = WSTOPSIG(wait_stat); /* signal that caused the stop */
|
||
|
memcpy(buff, joberrs[joberr_stp].msg, joberrs[joberr_stp].len);
|
||
|
c = i2asc(&buff[joberrs[joberr_stp].len], stop_sig);
|
||
|
rts_error(VARLSTCNT(6) ERR_JOBFAIL, 0, ERR_TEXT, 2, c - buff, buff);
|
||
|
} else if (WIFEXITED(wait_stat)) /* child EXITed normally */
|
||
|
{
|
||
|
if (exit_stat < joberr_stp) /* one of our EXITs */
|
||
|
{
|
||
|
rts_error(VARLSTCNT(6) ERR_JOBFAIL, 0, ERR_TEXT, 2,
|
||
|
joberrs[exit_stat].len, joberrs[exit_stat].msg);
|
||
|
} else /* unknown exit status */
|
||
|
{
|
||
|
assert(FALSE);
|
||
|
util_out_print("Unknown exit status !UL (status = !UL)", TRUE, exit_stat, status);
|
||
|
rts_error(VARLSTCNT(6) ERR_JOBFAIL, 0, ERR_TEXT, 2,
|
||
|
joberrs[joberr_gen].len, joberrs[joberr_gen].msg);
|
||
|
}
|
||
|
} else
|
||
|
{
|
||
|
assert(FALSE);
|
||
|
util_out_print("Unknown wait status !UL", TRUE, status);
|
||
|
rts_error(VARLSTCNT(6) ERR_JOBFAIL, 0, ERR_TEXT, 2,
|
||
|
joberrs[joberr_gen].len, joberrs[joberr_gen].msg);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
} else
|
||
|
{
|
||
|
if (timed)
|
||
|
dollar_truth = 1;
|
||
|
|
||
|
assert(0 < zjob_pid);
|
||
|
dollar_zjob = zjob_pid;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE; /* This will never get executed, added to make compiler happy */
|
||
|
}
|