353 lines
11 KiB
C
353 lines
11 KiB
C
|
/****************************************************************
|
||
|
* *
|
||
|
* Copyright 2001, 2011 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 <sys/types.h>
|
||
|
#include <errno.h>
|
||
|
#include "gtm_string.h"
|
||
|
#include "gtm_fcntl.h"
|
||
|
#include "gtm_unistd.h"
|
||
|
#include "gtm_stat.h"
|
||
|
#include "gtm_stdio.h"
|
||
|
|
||
|
#include "rtnhdr.h"
|
||
|
#include "zroutines.h"
|
||
|
#include "compiler.h"
|
||
|
#include "srcline.h"
|
||
|
#include "gtmio.h"
|
||
|
#include "eintr_wrappers.h"
|
||
|
#include "op.h"
|
||
|
#include "zbreak.h"
|
||
|
#include "hashtab_mname.h"
|
||
|
#ifdef GTM_TRIGGER
|
||
|
# include "gdsroot.h"
|
||
|
# include "gtm_facility.h"
|
||
|
# include "fileinfo.h"
|
||
|
# include "gdsbt.h"
|
||
|
# include "gdsfhead.h"
|
||
|
# include "gv_trigger.h"
|
||
|
# include "cdb_sc.h"
|
||
|
# include "t_retry.h"
|
||
|
# include "trigger_source_read_andor_verify.h"
|
||
|
#endif
|
||
|
|
||
|
#define RT_TBL_SZ 20
|
||
|
|
||
|
GBLREF uint4 dollar_tlevel;
|
||
|
GBLREF unsigned int t_tries;
|
||
|
|
||
|
LITDEF char litconst_space = ' ';
|
||
|
|
||
|
error_def(ERR_TXTSRCFMT);
|
||
|
error_def(ERR_SYSCALL);
|
||
|
|
||
|
int get_src_line(mval *routine, mval *label, int offset, mstr **srcret, boolean_t verifytrig)
|
||
|
{
|
||
|
int srcrecs, *lt_ptr, size, line_indx, srcfilnamlen;
|
||
|
uint4 checksum;
|
||
|
boolean_t found, added, eof_seen, srcstat;
|
||
|
mstr src;
|
||
|
rhdtyp *rtn_vector;
|
||
|
zro_ent *srcdir;
|
||
|
mstr *base, *current, *top;
|
||
|
char buff[MAX_SRCLINE], *cptr, *srcfile_name;
|
||
|
char srcnamebuf[SIZEOF(mident_fixed) + STR_LIT_LEN(DOTM)];
|
||
|
ht_ent_mname *tabent;
|
||
|
var_tabent rtnent;
|
||
|
routine_source *src_tbl;
|
||
|
int rc, fclose_res;
|
||
|
char *fgets_rc;
|
||
|
FILE *fp;
|
||
|
struct stat srcfile_stat;
|
||
|
off_t srcsize;
|
||
|
unsigned char *srcptr, *srcptr_max, *srcstart;
|
||
|
# ifdef GTM_TRIGGER
|
||
|
boolean_t is_trigger;
|
||
|
# endif
|
||
|
DCL_THREADGBL_ACCESS;
|
||
|
|
||
|
SETUP_THREADGBL_ACCESS;
|
||
|
srcstat = 0;
|
||
|
*srcret = NULL;
|
||
|
if (NULL == (TREF(rt_name_tbl)).base)
|
||
|
init_hashtab_mname(TADR(rt_name_tbl), RT_TBL_SZ, HASHTAB_COMPACT, HASHTAB_SPARE_TABLE);
|
||
|
assert (routine->mvtype & MV_STR);
|
||
|
|
||
|
/* The source to be loaded can be of two types:
|
||
|
*
|
||
|
* 1. Normal routine source to be looked up on disk
|
||
|
* 2. Trigger source located in a database.
|
||
|
*
|
||
|
* Determine which source we need.
|
||
|
*/
|
||
|
GTMTRIG_ONLY(IS_TRIGGER_RTN(&routine->str, is_trigger));
|
||
|
/* Need source on a trigger. Get trigger source loaded and/or verified which may involve
|
||
|
* creating a TP fence and dealing with TP restarts.
|
||
|
*/
|
||
|
# ifdef GTM_TRIGGER
|
||
|
if (is_trigger)
|
||
|
{ /* ZPRINT wants a consistent view of triggers across multiple calls so it bypasses verification
|
||
|
* after the first call. In this case, the trigger had better be found since the first call found it.
|
||
|
*/
|
||
|
if (verifytrig)
|
||
|
{
|
||
|
rc = trigger_source_read_andor_verify(&routine->str, TRIGGER_SRC_LOAD);
|
||
|
if (0 != rc)
|
||
|
return SRCNOTAVAIL;
|
||
|
}
|
||
|
rtn_vector = find_rtn_hdr(&routine->str); /* Trigger routine should be loaded now */
|
||
|
if (NULL == rtn_vector)
|
||
|
{
|
||
|
assert(verifytrig); /* Noverify trig should always be found */
|
||
|
return SRCNOTAVAIL; /* Could happen if trigger name got modified or uniqueified */
|
||
|
}
|
||
|
} else
|
||
|
# endif
|
||
|
{
|
||
|
if (NULL == (rtn_vector = find_rtn_hdr(&routine->str))) /* Note assignment */
|
||
|
{
|
||
|
op_zlink(routine, NULL);
|
||
|
rtn_vector = find_rtn_hdr(&routine->str);
|
||
|
if (NULL == rtn_vector)
|
||
|
return OBJMODMISS;
|
||
|
}
|
||
|
}
|
||
|
if (!rtn_vector->src_full_name.len)
|
||
|
return SRCNOTAVAIL;
|
||
|
rtnent.var_name = rtn_vector->routine_name;
|
||
|
COMPUTE_HASH_MNAME(&rtnent);
|
||
|
added = add_hashtab_mname(TADR(rt_name_tbl), &rtnent, NULL, &tabent);
|
||
|
src_tbl = (routine_source *)tabent->value;
|
||
|
if (added || (NULL == tabent->value))
|
||
|
{
|
||
|
checksum = 0;
|
||
|
# ifdef GTM_TRIGGER
|
||
|
if (is_trigger)
|
||
|
{
|
||
|
srcstart = (unsigned char *)((gv_trigger_t *)rtn_vector->trigr_handle)->xecute_str.str.addr;
|
||
|
srcsize = ((gv_trigger_t *)rtn_vector->trigr_handle)->xecute_str.str.len;
|
||
|
assert(0 < srcsize);
|
||
|
assert(NULL != srcstart);
|
||
|
srcrecs = (int)rtn_vector->lnrtab_len;
|
||
|
/* Allocate the array to hold the mstr array pointing to the src lines. */
|
||
|
src_tbl = (routine_source *)malloc(SIZEOF(routine_source) + ((srcrecs - 1) * SIZEOF(mstr)));
|
||
|
src_tbl->srcbuff = srcstart;
|
||
|
src_tbl->srcrecs = srcrecs;
|
||
|
/* Remove the source buffer from the trigger descriptor so nobody frees it inappropriately */
|
||
|
((gv_trigger_t *)rtn_vector->trigr_handle)->xecute_str.str.len = 0;
|
||
|
((gv_trigger_t *)rtn_vector->trigr_handle)->xecute_str.str.addr = NULL;
|
||
|
/* Parse code setting the mstrs for each line encountered */
|
||
|
srcptr = srcstart;
|
||
|
srcptr_max = srcptr + srcsize;
|
||
|
for (current = (src_tbl->srclines + 1), top = current + (srcrecs - 1); current < top; current++)
|
||
|
{
|
||
|
assert(srcptr < srcptr_max);
|
||
|
current->addr = (char *)srcstart;
|
||
|
/* Find end of this record */
|
||
|
for (; (srcptr < srcptr_max) && ('\n' != *srcptr); srcptr++)
|
||
|
;
|
||
|
if (0 != (size = (srcptr - (unsigned char *)current->addr))) /* note assignment */
|
||
|
{ /* Do checksum computation plus set length */
|
||
|
RTN_SRC_CHKSUM((char *)srcstart, size, checksum);
|
||
|
current->len = size;
|
||
|
} else
|
||
|
{ /* Null record - point to a single space for the record */
|
||
|
current->addr = (char *)&litconst_space;
|
||
|
current->len = 1;
|
||
|
}
|
||
|
srcptr++; /* Skip line end char */
|
||
|
srcstart = srcptr;
|
||
|
}
|
||
|
if (checksum != rtn_vector->checksum)
|
||
|
{ /* Should never happen with a trigger unless it ran into some restartable concurrency
|
||
|
* issues. Assert we can restart and do it.
|
||
|
*/
|
||
|
if (0 < dollar_tlevel)
|
||
|
{
|
||
|
assert(CDB_STAGNATE > t_tries);
|
||
|
t_retry(cdb_sc_triggermod);
|
||
|
} else
|
||
|
GTMASSERT;
|
||
|
}
|
||
|
} else
|
||
|
# endif
|
||
|
{
|
||
|
srcfile_name = malloc(rtn_vector->src_full_name.len + 1);
|
||
|
memcpy(srcfile_name, rtn_vector->src_full_name.addr, rtn_vector->src_full_name.len);
|
||
|
*(srcfile_name + rtn_vector->src_full_name.len) = 0; /* ensure string is null terminated */
|
||
|
/* At this point, it is not clear if Fopen will handle zos tagging correctly in all cases.
|
||
|
* especially when tagged with other than ISO8859-1 or IBM-1047. When we resurrect the zOS
|
||
|
* platform, we need to test this out.
|
||
|
*/
|
||
|
fp = Fopen(srcfile_name, "r");
|
||
|
if (NULL == fp)
|
||
|
{
|
||
|
free(srcfile_name);
|
||
|
srcfile_name = NULL;
|
||
|
srcfilnamlen = (int)rtn_vector->routine_name.len;
|
||
|
memcpy(srcnamebuf, rtn_vector->routine_name.addr, srcfilnamlen);
|
||
|
if (srcnamebuf[0] == '%') /* percents are translated to _ on filenames */
|
||
|
srcnamebuf[0] = '_';
|
||
|
MEMCPY_LIT(&srcnamebuf[srcfilnamlen], DOTM);
|
||
|
src.addr = srcnamebuf;
|
||
|
src.len = INTCAST(srcfilnamlen + STR_LIT_LEN(DOTM));
|
||
|
zro_search (0, 0, &src, &srcdir, TRUE);
|
||
|
if (srcdir)
|
||
|
{
|
||
|
srcfile_name = malloc(src.len + srcdir->str.len + 2);
|
||
|
memcpy(srcfile_name, srcdir->str.addr, srcdir->str.len);
|
||
|
cptr = srcfile_name + srcdir->str.len;
|
||
|
*cptr++ = '/';
|
||
|
memcpy(cptr, src.addr, src.len);
|
||
|
cptr += src.len;
|
||
|
*cptr++ = 0;
|
||
|
fp = Fopen(srcfile_name, "r");
|
||
|
if (NULL == fp)
|
||
|
{
|
||
|
rts_error(VARLSTCNT(1) errno);
|
||
|
assert(FALSE);
|
||
|
}
|
||
|
found = TRUE;
|
||
|
} else
|
||
|
found = FALSE;
|
||
|
} else
|
||
|
found = TRUE;
|
||
|
|
||
|
if (!found)
|
||
|
{
|
||
|
srcstat |= SRCNOTFND;
|
||
|
srcrecs = 0;
|
||
|
srcsize = 0;
|
||
|
} else
|
||
|
{
|
||
|
srcrecs = (int)rtn_vector->lnrtab_len;
|
||
|
/* Find out how big the file is so we can allocate the memory in one shot */
|
||
|
if ((NULL != fp) && !(srcstat & (SRCNOTFND | SRCNOTAVAIL)))
|
||
|
{
|
||
|
rc = stat(srcfile_name, &srcfile_stat);
|
||
|
if (0 != rc)
|
||
|
{
|
||
|
free(srcfile_name);
|
||
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("stat"), CALLFROM, rc);
|
||
|
}
|
||
|
srcsize = srcfile_stat.st_size;
|
||
|
} else
|
||
|
srcsize = 0;
|
||
|
}
|
||
|
if (NULL != srcfile_name)
|
||
|
free(srcfile_name);
|
||
|
assert((found && srcrecs >= 1) || (srcrecs == 0));
|
||
|
|
||
|
/* Allocate source mstr structure. Since structure has one mstr in it, allocate one less.
|
||
|
* Note, the size we get from lnrtab_len has an extra [0] origin entry in the total. This
|
||
|
* entry is not used in the source array for direct referencing ease.
|
||
|
*/
|
||
|
src_tbl = (routine_source *)malloc(SIZEOF(routine_source) + ((srcrecs - 1) * SIZEOF(mstr)));
|
||
|
src_tbl->srcbuff = (0 < srcsize) ? malloc(srcsize) : NULL;
|
||
|
base = src_tbl->srclines;
|
||
|
srcptr = src_tbl->srcbuff;
|
||
|
DEBUG_ONLY(srcptr_max = srcptr + srcsize);
|
||
|
src_tbl->srcrecs = srcrecs;
|
||
|
eof_seen = FALSE;
|
||
|
for (current = base + 1, top = base + srcrecs ; current < top ; current++)
|
||
|
{
|
||
|
assert(found && (NULL != fp));
|
||
|
if (!eof_seen)
|
||
|
{
|
||
|
FGETS(buff, MAX_SRCLINE, fp, fgets_rc);
|
||
|
if (NULL == fgets_rc)
|
||
|
{
|
||
|
if (ferror(fp))
|
||
|
{
|
||
|
FCLOSE(fp, fclose_res);
|
||
|
assert(!fclose_res);
|
||
|
rts_error(VARLSTCNT(3) ERR_TXTSRCFMT, 0, errno);
|
||
|
assert(FALSE);
|
||
|
} else
|
||
|
{
|
||
|
eof_seen = TRUE;
|
||
|
assert(feof(fp));
|
||
|
size = 0;
|
||
|
}
|
||
|
} else
|
||
|
{
|
||
|
size = (int)STRLEN(buff);
|
||
|
/* Strip trailing '\n' if any (if at least one byte was read in) */
|
||
|
if (size && ('\n' == buff[size - 1]))
|
||
|
size--;
|
||
|
}
|
||
|
} else /* eof seen; nothing more to read in file */
|
||
|
size = 0;
|
||
|
if (size)
|
||
|
{ /* Calculate checksum to verify with loaded routine */
|
||
|
RTN_SRC_CHKSUM(buff, size, checksum);
|
||
|
assert(NULL != srcptr);
|
||
|
assert((srcptr + size) <= srcptr_max);
|
||
|
current->len = size;
|
||
|
current->addr = (char *)srcptr;
|
||
|
memcpy(srcptr, buff, size);
|
||
|
srcptr += size;
|
||
|
} else
|
||
|
{
|
||
|
current->addr = (char *)&litconst_space;
|
||
|
current->len = 1;
|
||
|
}
|
||
|
}
|
||
|
if (found)
|
||
|
{
|
||
|
*base = *(base + 1);
|
||
|
/* Ensure we have reached the end of the source file. If not, we need to issue a CHECKSUMFAIL
|
||
|
* error. Most often the !eof_seen part of the check is not needed since the checksums will not
|
||
|
* match. But if it so happens that the checksums do match, then this extra check helps us
|
||
|
* correctly identify a TXTSRCMAT error.
|
||
|
*/
|
||
|
if (!eof_seen)
|
||
|
{
|
||
|
FGETS(buff, MAX_SRCLINE, fp, fgets_rc);
|
||
|
if ((NULL == fgets_rc) && !ferror(fp))
|
||
|
{
|
||
|
eof_seen = TRUE;
|
||
|
assert(feof(fp));
|
||
|
}
|
||
|
}
|
||
|
if (!eof_seen || (checksum != rtn_vector->checksum))
|
||
|
srcstat |= CHECKSUMFAIL;
|
||
|
FCLOSE(fp, fclose_res);
|
||
|
assert(!fclose_res);
|
||
|
}
|
||
|
}
|
||
|
src_tbl->srcstat = srcstat;
|
||
|
tabent->value = (char *)src_tbl;
|
||
|
} else
|
||
|
srcstat |= src_tbl->srcstat;
|
||
|
lt_ptr = (int *)find_line_addr(rtn_vector, &label->str, 0, NULL);
|
||
|
if (!lt_ptr)
|
||
|
srcstat |= LABELNOTFOUND;
|
||
|
else if (!(srcstat & (SRCNOTFND | SRCNOTAVAIL)))
|
||
|
{
|
||
|
line_indx = (int)(lt_ptr - (int *)LNRTAB_ADR(rtn_vector));
|
||
|
line_indx += offset;
|
||
|
if (line_indx == 0)
|
||
|
srcstat |= ZEROLINE;
|
||
|
else if (line_indx < 0)
|
||
|
srcstat |= NEGATIVELINE;
|
||
|
else if (line_indx >= rtn_vector->lnrtab_len)
|
||
|
srcstat |= AFTERLASTLINE;
|
||
|
else /* successfully located line */
|
||
|
{
|
||
|
*srcret = &src_tbl->srclines[line_indx];
|
||
|
/* DBGFPF((stderr, "get_src_line: returning string %.*s\n", (*srcret)->len, (*srcret)->addr)); */
|
||
|
}
|
||
|
}
|
||
|
return srcstat;
|
||
|
}
|