1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
|
/****************************************************************
* *
* Copyright (c) 2010-2024 Fidelity National Information *
* Services, Inc. and/or its subsidiaries. All rights reserved. *
* *
* 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 "gtm_stdlib.h"
#include "gtm_string.h"
#include "gtm_limits.h"
#include "gtm_stdio.h"
#include "gtm_unistd.h"
#include <errno.h>
#include "cmd_qlf.h"
#include "compiler.h"
#include "error.h"
#include <rtnhdr.h>
#include "stack_frame.h"
#include "lv_val.h"
#include "mv_stent.h"
#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gdscc.h"
#include "gdskill.h"
#include "jnl.h"
#include "gv_trigger.h"
#include "gtm_trigger.h"
#include "trigger.h"
#include "op.h"
#include "gtmio.h"
#include "stringpool.h"
#include "alias.h"
#include "urx.h"
#include "zbreak.h"
#include "gtm_text_alloc.h"
#include "buddy_list.h" /* needed for tp.h */
#include "tp.h"
#include "tp_frame.h"
#include "gvname_info.h"
#include "op_merge.h"
#include "golevel.h"
#include "flush_jmp.h"
#include "dollar_zlevel.h"
#include "gtmimagename.h"
#include "wbox_test_init.h"
#include "have_crit.h"
#include "srcline.h"
#include "zshow.h"
#include "zwrite.h"
#include "zr_unlink_rtn.h"
#ifdef GTM_TRIGGER
#define EMBED_SOURCE_PARM " -EMBED_SOURCE "
#define ERROR_CAUSING_JUNK "XX XX XX XX"
#define NEWLINE "\n"
#define OBJECT_PARM " -OBJECT="
#define OBJECT_FTYPE DOTOBJ
#define NAMEOFRTN_PARM " -NAMEOFRTN="
#define S_CUTOFF 7
#define GTM_TRIGGER_SOURCE_NAME "GTM Trigger"
#define MAX_MKSTEMP_RETRIES 100
GBLREF boolean_t run_time;
GBLREF mv_stent *mv_chain;
GBLREF stack_frame *frame_pointer;
GBLREF uint4 trigger_name_cntr;
GBLREF int dollar_truth;
GBLREF mstr extnam_str;
GBLREF mval dollar_zsource;
GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn;
GBLREF symval *curr_symval;
GBLREF int4 gtm_trigger_depth;
GBLREF int4 tstart_trigger_depth;
GBLREF mstr *dollar_ztname;
GBLREF mval *dollar_ztdata;
GBLREF mval *dollar_ztdelim;
GBLREF mval *dollar_ztoldval;
GBLREF mval *dollar_ztriggerop;
GBLREF mval *dollar_ztupdate;
GBLREF mval *dollar_ztvalue;
GBLREF boolean_t *ztvalue_changed_ptr;
GBLREF rtn_tabent *rtn_names, *rtn_names_end;
GBLREF boolean_t ztrap_explicit_null;
GBLREF int mumps_status;
GBLREF tp_frame *tp_pointer;
GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */
GBLREF uint4 dollar_tlevel;
GBLREF symval *trigr_symval_list;
GBLREF trans_num local_tn;
GBLREF int merge_args;
GBLREF zwr_hash_table *zwrhtab;
#ifdef DEBUG
GBLREF ch_ret_type (*ch_at_trigger_init)();
GBLREF boolean_t donot_INVOKE_MUMTSTART;
/* For debugging purposes - since a stack unroll does not let us see past the current GTM invocation, knowing
* what these parms are can be the determining factor in debugging an issue -- knowing what gtm_trigger() is
* attempting. For that reason, these values are also saved/restored.
*/
GBLREF gv_trigger_t *gtm_trigdsc_last;
GBLREF gtm_trigger_parms *gtm_trigprm_last;
GBLREF mval dollar_ztwormhole;
#endif
LITREF mval literal_null;
LITREF char alphanumeric_table[];
LITREF int alphanumeric_table_len;
STATICDEF int4 gtm_trigger_comp_prev_run_time;
error_def(ERR_ASSERT);
error_def(ERR_FILENOTFND);
error_def(ERR_GTMASSERT);
error_def(ERR_GTMASSERT2);
error_def(ERR_GTMCHECK);
error_def(ERR_LABELUNKNOWN);
error_def(ERR_MAXTRIGNEST);
error_def(ERR_MEMORY);
error_def(ERR_OUTOFSPACE);
error_def(ERR_REPEATERROR);
error_def(ERR_STACKCRIT);
error_def(ERR_STACKOFLOW);
error_def(ERR_SYSCALL);
error_def(ERR_TEXT);
error_def(ERR_TPRETRY);
error_def(ERR_TRIGCOMPFAIL);
error_def(ERR_TRIGNAMEUNIQ);
error_def(ERR_TRIGTLVLCHNG);
error_def(ERR_CITPNESTED);
/* Macro to re-initialize a symval block that was on a previously-used free chain */
#define REINIT_SYMVAL_BLK(svb, prev) \
{ \
symval *ptr; \
lv_blk *lvbp; \
lv_val *lv_base, *lv_free; \
\
ptr = svb; \
assert(NULL == ptr->xnew_var_list); \
assert(NULL == ptr->xnew_ref_list); \
reinitialize_hashtab_mname(&ptr->h_symtab); \
ptr->lv_flist = NULL; \
ptr->tp_save_all = 0; \
ptr->alias_activity = FALSE; \
ptr->last_tab = (prev); \
ptr->symvlvl = prev->symvlvl + 1; \
/* The lv_blk chain can remain as is but need to reinit each block so no elements are "used" */ \
for (lvbp = ptr->lv_first_block; lvbp; lvbp = lvbp->next) \
{ /* Likely only one of these blocks (some few lvvals) but loop in case.. */ \
lv_base = (lv_val *)LV_BLK_GET_BASE(lvbp); \
lv_free = LV_BLK_GET_FREE(lvbp, lv_base); \
clrlen = INTCAST((char *)lv_free - (char *)lv_base); \
if (0 != clrlen) \
{ \
memset(lv_base, '\0', clrlen); \
lvbp->numUsed = 0; \
} \
} \
}
/* All other platforms use this much faster direct return */
void gtm_levl_ret_code(void);
STATICFNDEF int gtm_trigger_invoke(void);
/* gtm_trigger - saves (some of) current environment, sets up new environment and drives a trigger.
*
* Triggers are one of two places where compiled M code is driven while the C stack is not at a constant level.
* The other place that does this is call-ins. Because this M code invocation needs to be separate from other
* running code, a new running environment is setup with its own base frame to prevent random unwinding back
* into earlier levels. All returns from the invoked generated code come back through gtm_trigger_invoke() with
* the exception of error handling looking for a handler or not having an error "handled" (clearing $ECODE) can
* just keep unwinding until all trigger levels are gone.
*
* Trigger names:
*
* Triggers have a base name set by MUPIP TRIGGER in the TRIGNAME hasht entry which is read by gv_trigger.c and
* passed to us. If it collides with an existing trigger name, we add some suffixing to it (up to two chars)
* and create it with that name.
*
* Trigger compilation:
*
* - When a trigger is presented to us for the first time, it needs to be compiled. We do this by writing it out
* using a system generated unique name to a temp file and compiling it with the -NAMEOFRTN parameter which
* sets the name of the routine different than the unique random object name.
* - The file is then linked in and its address recorded so the compilation only happens once.
*
* Trigger M stack format:
*
* - First created frame is a "base frame" (created by base_frame). This frame is set up to return to us
* (the caller) and has no backchain (old_frame_pointer is null). It also has the type SFT_TRIGR | SFT_COUNT
* so it is a counted frame (it is important to be counted so the mv_stents we create don't try to backup to
* a previous counted frame.
* - The second created frame is for the trigger being executed. We fill in the stack_frame from the trigger
* description and then let it rip by calling dm_start(). When the trigger returns through the base frame
* which calls gtm_levl_ret_code and pulls the return address of our call to dm_start off the stack and
* unwinds the appropriate saved regs, it returns back to us.
*
* Error handling in a trigger frame:
*
* - $ETRAP only. $ZTRAP is forbidden. Standard rules apply.
* - Error handling does not return to the trigger base frame but unwinds the base frame doing a rollback if
* necessary.
*/
CONDITION_HANDLER(gtm_trigger_complink_ch)
{ /* Condition handler for trigger compilation and link - be noisy but don't error out. Note that compilations do
* have their own handler but other errors are still possible. The primary use of this handler is (1) to remove
* the mv_stent we created and (2) most importantly to turn off the trigger_compile_and_link flag.
*/
START_CH(TRUE);
TREF(trigger_compile_and_link) = FALSE;
run_time = gtm_trigger_comp_prev_run_time;
if (((unsigned char *)mv_chain == msp) && (MVST_MSAV == mv_chain->mv_st_type)
&& (&dollar_zsource == mv_chain->mv_st_cont.mvs_msav.addr))
{ /* Top mv_stent is one we pushed on there - get rid of it */
dollar_zsource = mv_chain->mv_st_cont.mvs_msav.v;
POP_MV_STENT();
}
if (DUMPABLE)
/* Treat fatal errors thusly for a ch that may give better diagnostics */
NEXTCH;
if (ERR_TRIGCOMPFAIL != SIGNAL)
{
/* Echo error message if not general trigger compile failure message (which gtm_trigger outputs anyway */
PRN_ERROR;
}
if ((SUCCESS == SEVERITY) || (INFO == SEVERITY))
{ /* Just keep going for non-error issues */
CONTINUE;
}
UNWIND(NULL, NULL);
}
CONDITION_HANDLER(gtm_trigger_ch)
{ /* Condition handler for trigger execution - This handler is pushed on first for a given trigger level, then
* mdb_condition_handler is pushed on so will appear multiple times as trigger depth increases. There is
* always an mdb_condition_handler behind us for an earlier trigger level and we let it handle severe
* errors for us as it gives better diagnostics (e.g. GTM_FATAL_ERROR dumps) in addition to the file core dump.
*/
START_CH(TRUE); /* Note: "prev_intrpt_state" variable is defined/declared inside START_CH macro */
DBGTRIGR((stderr, "gtm_trigger_ch: Failsafe condition cond handler entered with SIGNAL = %d\n", SIGNAL));
if (DUMPABLE)
/* Treat fatal errors thusly */
NEXTCH;
if ((SUCCESS == SEVERITY) || (INFO == SEVERITY))
{ /* Just keep going for non-error issues */
CONTINUE;
}
mumps_status = SIGNAL;
/* We are about to no longer have a trigger stack frame and thus re-enter trigger no-mans-land */
DEFER_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND, prev_intrpt_state);
assert(INTRPT_OK_TO_INTERRUPT == prev_intrpt_state); /* relied upon by ENABLE_INTERRUPTS in "gtm_trigger_invoke" */
gtm_trigger_depth--; /* Bypassing gtm_trigger_invoke() so do maint on depth indicator */
assert(0 <= gtm_trigger_depth);
/* Return back to gtm_trigger with error code */
UNWIND(NULL, NULL);
}
STATICFNDEF int gtm_trigger_invoke(void)
{ /* Invoke trigger M routine. Separate so error returns to gtm_trigger with proper retcode */
int rc;
intrpt_state_t prev_intrpt_state;
ESTABLISH_RET(gtm_trigger_ch, mumps_status);
gtm_trigger_depth++;
DBGTRIGR((stderr, "gtm_trigger: Dispatching trigger at depth %d\n", gtm_trigger_depth));
assert(0 < gtm_trigger_depth);
assert(GTM_TRIGGER_DEPTH_MAX >= gtm_trigger_depth);
/* Allow interrupts to occur while the trigger is running.
* Normally we would have the new state stored in "prev_intrpt_state" but that is not possible here because
* the corresponding DEFER_INTERRUPTS happened in "gtm_trigger" or a different call to "gtm_trigger_invoke"
* (in both cases, a different function) so we have an assert there that the previous state was INTRPT_OK_TO_INTERRUPT
* and use that instead of prev_intrpt_state here.
*/
ENABLE_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND, INTRPT_OK_TO_INTERRUPT);
rc = dm_start();
/* Now that we no longer have a trigger stack frame, we are back in trigger no-mans-land */
DEFER_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND, prev_intrpt_state);
assert(INTRPT_OK_TO_INTERRUPT == prev_intrpt_state); /* relied upon by ENABLE_INTERRUPTS in "gtm_trigger_invoke" above */
gtm_trigger_depth--;
DBGTRIGR((stderr, "gtm_trigger: Trigger returns with rc %d\n", rc));
REVERT;
assert(frame_pointer->type & SFT_TRIGR);
assert(0 <= gtm_trigger_depth);
CHECKHIGHBOUND(ctxt);
CHECKLOWBOUND(ctxt);
CHECKHIGHBOUND(active_ch);
CHECKLOWBOUND(active_ch);
return rc;
}
int gtm_trigger_complink(gv_trigger_t *trigdsc, boolean_t dolink)
{
char rtnname[GTM_PATH_MAX], rtnname_template[GTM_PATH_MAX];
char objname[GTM_PATH_MAX];
char zcomp_parms[(GTM_PATH_MAX * 2) + SIZEOF(mident_fixed) + SIZEOF(OBJECT_PARM) + SIZEOF(NAMEOFRTN_PARM)
+ SIZEOF(EMBED_SOURCE_PARM)];
mstr save_zsource;
int rtnfd, rc, lenobjname, len, retry, save_errno, urc;
char *error_desc, *mident_suffix_p1, *mident_suffix_p2, *mident_suffix_top, *namesub1, *namesub2;
char *zcomp_parms_ptr, *zcomp_parms_top;
mval zlfile, zcompprm;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
DBGTRIGR_ONLY(memcpy(rtnname, trigdsc->rtn_desc.rt_name.addr, trigdsc->rtn_desc.rt_name.len));
DBGTRIGR_ONLY(rtnname[trigdsc->rtn_desc.rt_name.len] = 0);
DBGTRIGR((stderr, "gtm_trigger_complink: (Re)compiling trigger %s\n", rtnname));
ESTABLISH_RET(gtm_trigger_complink_ch, ((0 == error_condition) ? TREF(dollar_zcstatus) : error_condition));
/* Verify there are 2 available chars for uniqueness */
assert((MAX_MIDENT_LEN - TRIGGER_NAME_RESERVED_SPACE) >= (trigdsc->rtn_desc.rt_name.len));
assert(NULL == trigdsc->rtn_desc.rt_adr);
gtm_trigger_comp_prev_run_time = run_time;
run_time = TRUE; /* Required by compiler */
/* Verify the routine name set by MUPIP TRIGGER and read by gvtr_db_read_hasht() is not in use */
if (NULL != find_rtn_hdr(&trigdsc->rtn_desc.rt_name))
{ /* Ooops .. need name to be more unique.. */
namesub1 = trigdsc->rtn_desc.rt_name.addr + trigdsc->rtn_desc.rt_name.len++;
mident_suffix_top = (char *)alphanumeric_table + alphanumeric_table_len;
/* Phase 1. See if any single character can add uniqueness */
for (mident_suffix_p1 = (char *)alphanumeric_table; mident_suffix_p1 < mident_suffix_top; mident_suffix_p1++)
{
*namesub1 = *mident_suffix_p1;
if (NULL == find_rtn_hdr(&trigdsc->rtn_desc.rt_name))
break;
}
if (mident_suffix_p1 == mident_suffix_top)
{ /* Phase 2. Phase 1 could not find uniqueness .. Find it with 2 char variations */
namesub2 = trigdsc->rtn_desc.rt_name.addr + trigdsc->rtn_desc.rt_name.len++;
for (mident_suffix_p1 = (char *)alphanumeric_table; mident_suffix_p1 < mident_suffix_top;
mident_suffix_p1++)
{ /* First char loop */
for (mident_suffix_p2 = (char *)alphanumeric_table; mident_suffix_p2 < mident_suffix_top;
mident_suffix_p2++)
{ /* 2nd char loop */
*namesub1 = *mident_suffix_p1;
*namesub2 = *mident_suffix_p2;
if (NULL == find_rtn_hdr(&trigdsc->rtn_desc.rt_name))
{
mident_suffix_p1 = mident_suffix_top + 1; /* Break out of both loops */
break;
}
}
}
if (mident_suffix_p1 == mident_suffix_top)
{ /* Phase 3: Punt */
assert(WBTEST_HELPOUT_TRIGNAMEUNIQ == gtm_white_box_test_case_number);
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(5) ERR_TRIGNAMEUNIQ, 3,
trigdsc->rtn_desc.rt_name.len - 2, trigdsc->rtn_desc.rt_name.addr,
((alphanumeric_table_len + 1) * alphanumeric_table_len) + 1);
}
}
}
/* Write trigger execute string out to temporary file and compile it */
assert(MAX_XECUTE_LEN >= trigdsc->xecute_str.str.len);
assert(GTM_PATH_MAX >= (TREF(gtm_tmpdir)).len + SIZEOF("/trgtmpXXXXXX"));
rc = SNPRINTF(rtnname_template, GTM_PATH_MAX, "%.*s/trgtmpXXXXXX", (TREF(gtm_tmpdir)).len, (TREF(gtm_tmpdir)).addr);
assert(0 < rc); /* Note rc is return code aka length - we expect a non-zero length */
/* The mkstemp() routine is known to bogus-fail for no apparent reason at all especially on AIX 6.1. In the event
* this shortcoming plagues other platforms as well, we add a low-cost retry wrapper.
*/
retry = MAX_MKSTEMP_RETRIES;
do
{
strcpy(rtnname, rtnname_template);
MKSTEMP(rtnname, rtnfd);
} while ((-1 == rtnfd) && (EEXIST == errno) && (0 < --retry));
if (-1 == rtnfd)
{
save_errno = errno;
error_desc = STRERROR(save_errno);
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(8) ERR_FILENOTFND, 2, RTS_ERROR_TEXT(rtnname), ERR_TEXT, 2,
LEN_AND_STR(error_desc));
}
assert(0 < rtnfd); /* Verify file descriptor */
rc = 0;
# ifdef GEN_TRIGCOMPFAIL_ERROR
{ /* Used ONLY to generate an error in a trigger compile by adding some junk in a previous line */
DOWRITERC(rtnfd, ERROR_CAUSING_JUNK, strlen(ERROR_CAUSING_JUNK), rc); /* BYPASSOK */
if (0 != rc)
{
urc = UNLINK(rtnname);
assert(0 == urc);
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(7) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc);
}
}
# endif
DOWRITERC(rtnfd, trigdsc->xecute_str.str.addr, trigdsc->xecute_str.str.len, rc);
if (0 != rc)
{
urc = UNLINK(rtnname);
assert(0 == urc);
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(7) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc);
}
if (NULL == memchr(trigdsc->xecute_str.str.addr, '\n', trigdsc->xecute_str.str.len))
{
DOWRITERC(rtnfd, NEWLINE, strlen(NEWLINE), rc); /* BYPASSOK */
if (0 != rc)
{
urc = UNLINK(rtnname);
assert(0 == urc);
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(7) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc);
}
}
CLOSEFILE(rtnfd, rc);
if (0 != rc)
{
urc = UNLINK(rtnname);
assert(0 == urc);
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(7) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, rc);
}
assert(MAX_MIDENT_LEN > trigdsc->rtn_desc.rt_name.len);
zcomp_parms_ptr = zcomp_parms;
zcomp_parms_top = zcomp_parms + SIZEOF(zcomp_parms);
/* rt_name is not null terminated so start the compilation string like this */
MEMCPY_LIT(zcomp_parms_ptr, NAMEOFRTN_PARM);
zcomp_parms_ptr += STRLEN(NAMEOFRTN_PARM);
memcpy(zcomp_parms_ptr, trigdsc->rtn_desc.rt_name.addr, trigdsc->rtn_desc.rt_name.len);
zcomp_parms_ptr += trigdsc->rtn_desc.rt_name.len;
len = INTCAST(zcomp_parms_ptr - zcomp_parms);
/* Copy the rtnname to become object name while appending the null terminated OBJ file ext string */
lenobjname = SNPRINTF(objname, GTM_PATH_MAX, "%s" OBJECT_FTYPE, rtnname);
/* Append the remaining parameters to the compile string */
len += SNPRINTF(zcomp_parms_ptr, zcomp_parms_top - zcomp_parms_ptr,
" -OBJECT=%s -EMBED_SOURCE %s", objname, rtnname);
if (SIZEOF(zcomp_parms) <= len) /* overflow */
{
urc = UNLINK(rtnname);
assert(0 == urc);
assert(FALSE);
rts_error_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_TEXT, 2, RTS_ERROR_LITERAL("Compilation string too long"));
}
zcompprm.mvtype = MV_STR;
zcompprm.str.addr = zcomp_parms;
zcompprm.str.len = len;
/* Backup dollar_zsource so trigger doesn't show */
PUSH_MV_STENT(MVST_MSAV);
mv_chain->mv_st_cont.mvs_msav.v = dollar_zsource;
mv_chain->mv_st_cont.mvs_msav.addr = &dollar_zsource;
TREF(trigger_compile_and_link) = TRUE; /* Set flag so compiler knows this is a special trigger compile */
op_zcompile(&zcompprm, TRUE); /* Compile but don't use $ZCOMPILE qualifiers */
TREF(trigger_compile_and_link) = FALSE; /* compile_source_file() establishes handler so always returns */
if (0 != TREF(dollar_zcstatus))
{ /* Someone err'd.. */
run_time = gtm_trigger_comp_prev_run_time;
REVERT;
DEBUG_ONLY(send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(5) ERR_TEXT, 2,
RTS_ERROR_LITERAL("TRIGCOMPFAIL"), TREF(dollar_zcstatus)));
if (-1 == UNLINK(objname)) /* Delete the object file first since rtnname is the unique key */
DEBUG_ONLY(send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(11) ERR_SYSCALL, 5,
RTS_ERROR_LITERAL("unlink(objname)"), CALLFROM,
ERR_TEXT, 2, LEN_AND_STR(objname), errno));
if (-1 == UNLINK(rtnname)) /* Delete the source file */
DEBUG_ONLY(send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(11) ERR_SYSCALL, 5,
RTS_ERROR_LITERAL("unlink(rtnname)"), CALLFROM,
ERR_TEXT, 2, LEN_AND_STR(rtnname), errno));
return ERR_TRIGCOMPFAIL;
}
if (dolink)
{ /* Link is optional as MUPIP TRIGGER doesn't need link */
zlfile.mvtype = MV_STR;
zlfile.str.addr = objname;
zlfile.str.len = lenobjname;
/* Specifying literal_null for a second arg (as opposed to NULL or 0) allows us to specify
* linking the object file (no compilation or looking for source). The 2nd arg is parms for
* recompilation and is non-null in an explicit zlink which we need to emulate.
*/
# ifdef GEN_TRIGLINKFAIL_ERROR
UNLINK(objname); /* Delete object before it can be used */
# endif
TREF(trigger_compile_and_link) = TRUE; /* Overload flag so we know it is a trigger link */
op_zlink(&zlfile, (mval *)&literal_null); /* Need cast due to "extern const" attributes */
TREF(trigger_compile_and_link) = FALSE; /* If doesn't return, condition handler will clear */
/* No return here if link fails for some reason */
trigdsc->rtn_desc.rt_adr = find_rtn_hdr(&trigdsc->rtn_desc.rt_name);
/* Verify can find routine we just put there. Catastrophic if not */
assertpro(NULL != trigdsc->rtn_desc.rt_adr);
/* Replace the randomly generated source name with the constant "GTM Trigger" */
trigdsc->rtn_desc.rt_adr->src_full_name.addr = GTM_TRIGGER_SOURCE_NAME;
trigdsc->rtn_desc.rt_adr->src_full_name.len = STRLEN(GTM_TRIGGER_SOURCE_NAME);
trigdsc->rtn_desc.rt_adr->trigr_handle = trigdsc; /* Back pointer to trig def */
/* Release trigger source field since it was compiled with -embed_source */
assert(0 < trigdsc->xecute_str.str.len);
free(trigdsc->xecute_str.str.addr);
trigdsc->xecute_str.str.len = 0;
trigdsc->xecute_str.str.addr = NULL;
}
if (MVST_MSAV == mv_chain->mv_st_type && &dollar_zsource == mv_chain->mv_st_cont.mvs_msav.addr)
{ /* Top mv_stent is one we pushed on there - restore dollar_zsource and get rid of it */
dollar_zsource = mv_chain->mv_st_cont.mvs_msav.v;
POP_MV_STENT();
} else
assert(FALSE); /* This mv_stent should be the one we just pushed */
/* Remove temporary files created */
if (-1 == UNLINK(objname)) /* Delete the object file first since rtnname is the unique key */
DEBUG_ONLY(send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(11) ERR_SYSCALL, 5,
RTS_ERROR_LITERAL("unlink(objname)"), CALLFROM,
ERR_TEXT, 2, LEN_AND_STR(objname), errno));
if (-1 == UNLINK(rtnname)) /* Delete the source file */
DEBUG_ONLY(send_msg_csa(CSA_ARG(cs_addrs) VARLSTCNT(11) ERR_SYSCALL, 5,
RTS_ERROR_LITERAL("unlink(rtnname)"), CALLFROM,
ERR_TEXT, 2, LEN_AND_STR(rtnname), errno));
run_time = gtm_trigger_comp_prev_run_time;
REVERT;
return 0;
}
int gtm_trigger(gv_trigger_t *trigdsc, gtm_trigger_parms *trigprm)
{
mval *lvvalue;
lnr_tabent *lbl_offset_p;
uchar_ptr_t transfer_addr;
lv_val *lvval;
mname_entry *mne_p;
uint4 *indx_p;
ht_ent_mname *tabent;
boolean_t added;
int clrlen, rc, i, unwinds;
mval **lvvalarray;
mv_stent *mv_st_ent;
symval *new_symval;
uint4 dollar_tlevel_start;
stack_frame *fp;
intrpt_state_t prev_intrpt_state;
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
assert(!skip_dbtriggers); /* should not come here if triggers are not supposed to be invoked */
assert(trigdsc);
assert(trigprm);
assert((NULL != trigdsc->rtn_desc.rt_adr) || ((MV_STR & trigdsc->xecute_str.mvtype)
&& (0 != trigdsc->xecute_str.str.len)
&& (NULL != trigdsc->xecute_str.str.addr)));
assert(dollar_tlevel);
/* Determine if trigger needs to be compiled */
if (NULL == trigdsc->rtn_desc.rt_adr)
{ /* No routine hdr addr exists. Need to do compile */
if (0 != gtm_trigger_complink(trigdsc, TRUE))
{
PRN_ERROR; /* Leave record of what error caused the compilation failure if any */
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_TRIGCOMPFAIL, 2,
trigdsc->rtn_desc.rt_name.len - 1, trigdsc->rtn_desc.rt_name.addr);
}
}
assert(trigdsc->rtn_desc.rt_adr);
assert(trigdsc->rtn_desc.rt_adr == CURRENT_RHEAD_ADR(trigdsc->rtn_desc.rt_adr));
/* Setup trigger environment stack frame(s) for execution */
if (!(frame_pointer->type & SFT_TRIGR))
{ /* Create new trigger base frame first that back-stops stack unrolling and return to us */
if (GTM_TRIGGER_DEPTH_MAX < (gtm_trigger_depth + 1)) /* Verify we won't nest too deep */
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(3) ERR_MAXTRIGNEST, 1, GTM_TRIGGER_DEPTH_MAX);
DBGTRIGR((stderr, "gtm_trigger: Invoking new trigger at frame_pointer 0x%016lx ctxt value: 0x%016lx\n",
frame_pointer, ctxt));
/* Protect against interrupts while we have only a trigger base frame on the stack */
DEFER_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND, prev_intrpt_state);
assert(INTRPT_OK_TO_INTERRUPT == prev_intrpt_state); /* relied upon by ENABLE_INTERRUPTS in "gtm_trigger_invoke" */
/* The current frame invoked a trigger. We cannot return to it for a TP restart or other reason unless
* either the total operation (including trigger) succeeds and we unwind normally or unless the mpc is reset
* (like what happens in various error or restart conditions) because right now it returns to where a database
* command (KILL, SET or ZTRIGGER) was entered. Set flag in the frame to prevent MUM_TSTART unless the frame gets
* reset.
*/
frame_pointer->flags |= SSF_NORET_VIA_MUMTSTART; /* Do not return to this frame via MUM_TSTART */
DBGTRIGR((stderr, "gtm_trigger: Setting SSF_NORET_VIA_MUMTSTART in frame 0x"lvaddr"\n", frame_pointer));
base_frame(trigdsc->rtn_desc.rt_adr);
/* Finish base frame initialization - reset mpc/context to return to us without unwinding base frame */
frame_pointer->type |= SFT_TRIGR;
frame_pointer->mpc = CODE_ADDRESS(gtm_levl_ret_code);
frame_pointer->ctxt = GTM_CONTEXT(gtm_levl_ret_code);
/* This base stack frame is also where we save environmental info for all triggers invoked at this stack level.
* Subsequent triggers fired at this level in this trigger invocation need only reinitialize a few things but
* can avoid "the big save".
*/
if (NULL == trigr_symval_list)
{ /* No available symvals for use with this trigger, create one */
symbinit(); /* Initialize a symbol table the trigger will use */
curr_symval->trigr_symval = TRUE; /* Mark as trigger symval so will be saved not decommissioned */
} else
{ /* Trigger symval is available for reuse */
new_symval = trigr_symval_list;
assert(new_symval->trigr_symval);
trigr_symval_list = new_symval->last_tab; /* dequeue new curr_symval from list */
REINIT_SYMVAL_BLK(new_symval, curr_symval);
curr_symval = new_symval;
PUSH_MV_STENT(MVST_STAB);
mv_chain->mv_st_cont.mvs_stab = new_symval; /* So unw_mv_ent() can requeue it for later use */
}
/* Push our trigger environment save mv_stent onto the chain */
PUSH_MV_STENT(MVST_TRIGR);
mv_st_ent = mv_chain;
/* Initialize the mv_stent elements processed by stp_gcol which can be called by either op_gvsavtarg() or
* by the extnam saving code below. This initialization keeps stp_gcol - should it be called - from attempting
* to process unset fields filled with garbage in them as valid mstr address/length pairs.
*/
mv_st_ent->mv_st_cont.mvs_trigr.savtarg.str.len = 0;
mv_st_ent->mv_st_cont.mvs_trigr.savextref.len = 0;
mv_st_ent->mv_st_cont.mvs_trigr.dollar_etrap_save.str.len = 0;
mv_st_ent->mv_st_cont.mvs_trigr.dollar_ztrap_save.str.len = 0;
mv_st_ent->mv_st_cont.mvs_trigr.saved_dollar_truth = dollar_truth;
op_gvsavtarg(&mv_st_ent->mv_st_cont.mvs_trigr.savtarg);
if (extnam_str.len)
{
ENSURE_STP_FREE_SPACE(extnam_str.len);
mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr = (char *)stringpool.free;
memcpy(mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr, extnam_str.addr, extnam_str.len);
stringpool.free += extnam_str.len;
assert(stringpool.free <= stringpool.top);
}
mv_st_ent->mv_st_cont.mvs_trigr.savextref.len = extnam_str.len;
mv_st_ent->mv_st_cont.mvs_trigr.ztname_save = dollar_ztname;
mv_st_ent->mv_st_cont.mvs_trigr.ztdata_save = dollar_ztdata;
mv_st_ent->mv_st_cont.mvs_trigr.ztdelim_save = dollar_ztdelim;
mv_st_ent->mv_st_cont.mvs_trigr.ztoldval_save = dollar_ztoldval;
mv_st_ent->mv_st_cont.mvs_trigr.ztriggerop_save = dollar_ztriggerop;
mv_st_ent->mv_st_cont.mvs_trigr.ztupdate_save = dollar_ztupdate;
mv_st_ent->mv_st_cont.mvs_trigr.ztvalue_save = dollar_ztvalue;
mv_st_ent->mv_st_cont.mvs_trigr.ztvalue_changed_ptr = ztvalue_changed_ptr;
# ifdef DEBUG
/* In a debug process, these fields give clues of what trigger we are working on */
mv_st_ent->mv_st_cont.mvs_trigr.gtm_trigdsc_last_save = trigdsc;
mv_st_ent->mv_st_cont.mvs_trigr.gtm_trigprm_last_save = trigprm;
# endif
/* If this is a spanning node or spanning region update, a spanning node/region condition handler may be ahead.
* However, the handler just behind it should be either mdb_condition_handler or ch_at_trigger_init.
*/
assert(((0 == gtm_trigger_depth)
&& (((ch_at_trigger_init == ctxt->ch)
|| ((ch_at_trigger_init == (ctxt - 1)->ch)
&& ((&gvcst_put_ch == ctxt->ch) || (&gvcst_kill_ch == ctxt->ch)
|| (&gvcst_spr_kill_ch == ctxt->ch))))))
|| ((0 < gtm_trigger_depth)
&& (((&mdb_condition_handler == ctxt->ch)
|| ((&mdb_condition_handler == (ctxt - 1)->ch)
&& ((&gvcst_put_ch == ctxt->ch) || (&gvcst_kill_ch == ctxt->ch)
|| (&gvcst_spr_kill_ch == ctxt->ch)))))));
mv_st_ent->mv_st_cont.mvs_trigr.ctxt_save = ctxt;
mv_st_ent->mv_st_cont.mvs_trigr.gtm_trigger_depth_save = gtm_trigger_depth;
if (0 == gtm_trigger_depth)
{ /* Only back up $*trap settings when initiating the first trigger level */
mv_st_ent->mv_st_cont.mvs_trigr.dollar_etrap_save = TREF(dollar_etrap);
mv_st_ent->mv_st_cont.mvs_trigr.dollar_ztrap_save = TREF(dollar_ztrap);
mv_st_ent->mv_st_cont.mvs_trigr.ztrap_explicit_null_save = ztrap_explicit_null;
(TREF(dollar_ztrap)).str.len = 0;
ztrap_explicit_null = FALSE;
if (NULL != (TREF(gtm_trigger_etrap)).str.addr)
/* An etrap was defined for the trigger environment - Else existing $etrap persists */
TREF(dollar_etrap) = TREF(gtm_trigger_etrap);
}
mv_st_ent->mv_st_cont.mvs_trigr.mumps_status_save = mumps_status;
mv_st_ent->mv_st_cont.mvs_trigr.run_time_save = run_time;
/* See if a MERGE launched the trigger. If yes, save some state so ZWRITE, ZSHOW and/or MERGE can be
* run in the trigger we dispatch. */
PUSH_MVST_MRGZWRSV_IF_NEEDED;
mumps_status = 0;
run_time = TRUE; /* Previous value saved just above restored when frame pops */
} else
{ /* Trigger base frame exists so reinitialize the symbol table for new trigger invocation */
REINIT_SYMVAL_BLK(curr_symval, curr_symval->last_tab);
/* Locate the MVST_TRIGR mv_stent containing the backed up values. Some of those values need
* to be restored so the 2nd trigger has the same environment as the previous trigger at this level
*/
for (mv_st_ent = mv_chain;
(NULL != mv_st_ent) && (MVST_TRIGR != mv_st_ent->mv_st_type);
mv_st_ent = (mv_stent *)(mv_st_ent->mv_st_next + (char *)mv_st_ent))
;
assert(NULL != mv_st_ent);
assert((char *)mv_st_ent < (char *)frame_pointer); /* Ensure mv_stent associated this trigger frame */
/* Reinit backed up values from the trigger environment backup */
dollar_truth = mv_st_ent->mv_st_cont.mvs_trigr.saved_dollar_truth;
op_gvrectarg(&mv_st_ent->mv_st_cont.mvs_trigr.savtarg);
extnam_str.len = mv_st_ent->mv_st_cont.mvs_trigr.savextref.len;
if (extnam_str.len)
{
assert(0 < extnam_str.len);
/* It should be safe to free extnam_str.addr, which always looks to come from malloc(), and explicitly
and visibly malloc to the correct len here */
#ifdef STATIC_ANALYSIS
free(extnam_str.addr);
extnam_str.addr = (char *) malloc(extnam_str.len);
#endif
assert(extnam_str.addr);
assert(mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr);
memcpy(extnam_str.addr, mv_st_ent->mv_st_cont.mvs_trigr.savextref.addr, extnam_str.len);
}
mumps_status = 0;
assert(run_time);
/* Note we do not reset the handlers for parallel triggers - set one time only when enter first level
* trigger. After that, whatever happens in trigger world, stays in trigger world.
*/
}
assert(frame_pointer->type & SFT_TRIGR);
# ifdef DEBUG
gtm_trigdsc_last = trigdsc;
gtm_trigprm_last = trigprm;
# endif
/* Set new value of trigger ISVs. Previous values already saved in trigger base frame */
dollar_ztname = &trigdsc->rtn_desc.rt_name;
dollar_ztdata = (mval *)trigprm->ztdata_new;
dollar_ztdelim = (mval *)trigprm->ztdelim_new;
dollar_ztoldval = trigprm->ztoldval_new;
dollar_ztriggerop = (mval *)trigprm->ztriggerop_new;
dollar_ztupdate = trigprm->ztupdate_new;
dollar_ztvalue = trigprm->ztvalue_new;
ztvalue_changed_ptr = &trigprm->ztvalue_changed;
/* Set values associated with trigger into symbol table */
lvvalarray = trigprm->lvvalarray;
for (i = 0, mne_p = trigdsc->lvnamearray, indx_p = trigdsc->lvindexarray;
i < trigdsc->numlvsubs; ++i, ++mne_p, ++indx_p)
{ /* Once thru for each subscript we are to set */
lvvalue = lvvalarray[*indx_p]; /* Locate mval that contains value */
assert(NULL != lvvalue);
assert(MV_DEFINED(lvvalue)); /* No sense in defining the undefined */
lvval = lv_getslot(curr_symval); /* Allocate an lvval to put into symbol table */
LVVAL_INIT(lvval, curr_symval);
lvval->v = *lvvalue; /* Copy mval into lvval */
added = add_hashtab_mname_symval(&curr_symval->h_symtab, mne_p, lvval, &tabent);
assert(added);
assert(NULL != tabent);
}
/* While the routine header is available in trigdsc, we also need the <null> label address associated with
* the first (and only) line of code.
*/
lbl_offset_p = LNRTAB_ADR(trigdsc->rtn_desc.rt_adr);
transfer_addr = (uchar_ptr_t)LINE_NUMBER_ADDR(trigdsc->rtn_desc.rt_adr, lbl_offset_p);
/* Create new stack frame for invoked trigger in same fashion as gtm_init_env() creates its 2ndary frame */
# ifdef HAS_LITERAL_SECT
new_stack_frame(trigdsc->rtn_desc.rt_adr, (unsigned char *)LINKAGE_ADR(trigdsc->rtn_desc.rt_adr), transfer_addr);
# else
/* Any platform that does not follow pv-based linkage model either
* (1) uses the following calculation to determine the context pointer value, or
* (2) doesn't need a context pointer
*/
new_stack_frame(trigdsc->rtn_desc.rt_adr, PTEXT_ADR(trigdsc->rtn_desc.rt_adr), transfer_addr);
# endif
dollar_tlevel_start = dollar_tlevel;
assert(gv_target->gd_csa == cs_addrs);
gv_target->trig_local_tn = local_tn; /* Record trigger being driven for this global */
/* Invoke trigger generated code */
rc = gtm_trigger_invoke();
if (1 == rc)
{ /* Normal return code (from dm_start). Check if TP has been unwound or not */
assert(dollar_tlevel <= dollar_tlevel_start); /* Bigger would be quite the surprise */
if (dollar_tlevel < dollar_tlevel_start)
{ /* Our TP level was unwound during the trigger so throw an error */
DBGTRIGR((stderr, "gtm_trigger: $TLEVEL less than at start - throwing TRIGTLVLCHNG\n"));
gtm_trigger_fini(TRUE, FALSE); /* dump this trigger level */
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_TRIGTLVLCHNG, 2, trigdsc->rtn_desc.rt_name.len,
trigdsc->rtn_desc.rt_name.addr);
}
rc = 0; /* Be polite and return 0 for the (hopefully common) success case */
} else if (ERR_TPRETRY == rc)
{ /* We are restarting the entire transaction. There are two possibilities here:
* 1) This is a nested trigger level in which case we need to unwind further or
* the outer trigger level was created by M code. If either is true, just
* rethrow the TPRETRY error.
* 2) This is the outer trigger and the call to op_tstart() was done by our caller.
* In this case, we just return to our caller with a code signifying they need
* to restart the implied transaction.
*/
assert(dollar_tlevel && (tstart_trigger_depth <= gtm_trigger_depth));
if ((tstart_trigger_depth < gtm_trigger_depth) || !tp_pointer->implicit_tstart || !tp_pointer->implicit_trigger)
{ /* Unwind a trigger level to restart level or to next trigger boundary */
gtm_trigger_fini(FALSE, FALSE); /* Get rid of this trigger level - we won't be returning */
DBGTRIGR((stderr, "gtm_trigger: dm_start returned rethrow code - rethrowing ERR_TPRETRY\n"));
/* The bottommost mdb_condition handler better not be catching this restart if we did an implicit
* tstart. mdb_condition_handler will try to unwind further, and the process will inadvertently exit.
*/
assert((&mdb_condition_handler != ch_at_trigger_init)
|| ((&mdb_condition_handler == ctxt->ch) && (&mdb_condition_handler == chnd[1].ch)
&& (!tp_pointer->implicit_tstart || (&chnd[1] < ctxt))));
INVOKE_RESTART;
} else
{ /* It is possible we are restarting a transaction that never got around to creating a base
* frame yet the implicit TStart was done. So if there is no trigger base frame, do not
* run gtm_trigger_fini() but instead do the one piece of cleanup it does that we still need.
*/
assert(donot_INVOKE_MUMTSTART);
if (SFT_TRIGR & frame_pointer->type)
{ /* Normal case when TP restart unwinding back to implicit beginning */
gtm_trigger_fini(FALSE, FALSE);
DBGTRIGR((stderr, "gtm_trigger: dm_start returned rethrow code - returning to gvcst_<caller>\n"));
} else
{ /* Unusual case of trigger that died in no-mans-land before trigger base frame established.
* Remove the "do not return to me" flag only on non-error unwinds */
assert(tp_pointer->implicit_tstart);
assert(SSF_NORET_VIA_MUMTSTART & frame_pointer->flags);
frame_pointer->flags &= SSF_NORET_VIA_MUMTSTART_OFF;
DBGTRIGR((stderr, "gtm_trigger: turning off SSF_NORET_VIA_MUMTSTART (1) in frame 0x"lvaddr"\n",
frame_pointer));
DBGTRIGR((stderr, "gtm_trigger: unwinding no-base-frame trigger for TP restart\n"));
}
}
/* Fall out and return ERR_TPRETRY to caller */
} else
{ /* We should never get a return code of 0. This would be out-of-design and a signal that something
* is quite broken. We cannot "rethrow" outside the trigger because it was not initially an error so
* mdb_condition_handler would have no record of it (rethrown errors must have originally occurred in
* or to be RE-thrown) and assert fail at best.
*/
assertpro(0 != rc);
/* We have an unexpected return code due to some error during execution of the trigger that tripped
* gtm_trigger's safety handler (i.e. an error occurred in mdb_condition_handler() established by
* dm_start(). Since we are going to unwind the trigger frame and rethrow the error, we also have
* to unwind all the stack frames on top of the trigger frame. Figure out how many frames that is,
* unwind them all plus the trigger base frame before rethrowing the error.
*/
for (unwinds = 0, fp = frame_pointer; (NULL != fp) && !(SFT_TRIGR & fp->type); fp = fp->old_frame_pointer)
unwinds++;
assert((NULL != fp) && (SFT_TRIGR & fp->type));
GOFRAMES(unwinds, TRUE, FALSE);
assert((NULL != frame_pointer) && !(SFT_TRIGR & frame_pointer->type));
DBGTRIGR((stderr, "gtm_trigger: Unsupported return code (%d) - unwound %d frames and now rethrowing error\n",
rc, unwinds));
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(1) ERR_REPEATERROR);
}
return rc;
}
/* Unwind a trigger level, pop all the associated mv_stents and dispense with the base frame.
* Note that this unwind restores the condition handler stack pointer and correct gtm_trigger_depth value in
* order to maintain these values properly in the event of a major unwind. This routine is THE routine to use to unwind
* trigger base frames in all cases due to the cleanups it takes care of.
*/
void gtm_trigger_fini(boolean_t forced_unwind, boolean_t fromzgoto)
{
/* Would normally be an assert but potential frame stack damage so severe and resulting debug difficulty that we
* assertpro() instead.
*/
assertpro(frame_pointer->type & SFT_TRIGR);
/* Unwind the trigger base frame */
op_unwind();
/* restore frame_pointer stored at msp (see base_frame.c) */
frame_pointer = *(stack_frame**)msp;
msp += SIZEOF(stack_frame *); /* Remove frame save pointer from stack */
if (!forced_unwind)
{ /* Remove the "do not return to me" flag only on non-error unwinds. Note this flag may have already been
* turned off by an earlier tp_restart if this is not an implicit_tstart situation.
*/
assert(!tp_pointer->implicit_tstart || (SSF_NORET_VIA_MUMTSTART & frame_pointer->flags));
frame_pointer->flags &= SSF_NORET_VIA_MUMTSTART_OFF;
DBGTRIGR((stderr, "gtm_trigger_fini: turning off SSF_NORET_VIA_MUMTSTART(2) in frame 0x"lvaddr"\n", frame_pointer));
} else
{ /* Error unwind, make sure certain cleanups are done */
# ifdef DEBUG
assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth));
if (tstart_trigger_depth == gtm_trigger_depth) /* Unwinding gvcst_put() so get rid of flag it potentially set */
donot_INVOKE_MUMTSTART = FALSE;
# endif
/* Now that op_unwind has been done, it is possible "lvzwrite_block" got restored to pre-trigger state
* (if it was saved off in PUSH_MVST_MRGZWRSV call done at triggerland entry). If so, given this is a
* forced unwind of triggerland, clear any context corresponding to MERGE/ZSHOW that caused us to enter
* triggerland in the first place.
*/
NULLIFY_MERGE_ZWRITE_CONTEXT;
if (tp_pointer)
{ /* This TP transaction can never be allowed to commit if this is the first trigger
* (see comment in tp_frame.h against "cannot_commit" field for details).
*/
if ((0 == gtm_trigger_depth) && !fromzgoto)
{
DBGTRIGR((stderr, "gtm_trigger: cannot_commit flag set to TRUE\n"))
tp_pointer->cannot_commit = TRUE;
}
if ((tp_pointer->fp == frame_pointer) && tp_pointer->implicit_tstart)
OP_TROLLBACK(-1); /* We just unrolled the implicitly started TSTART so unroll what it did */
}
}
DBGTRIGR((stderr, "gtm_trigger: Unwound to trigger invoking frame: frame_pointer 0x%016lx ctxt value: 0x%016lx\n",
frame_pointer, ctxt));
/* Re-allow interruptions now that our base frame is gone.
* Normally we would have the new state stored in "prev_intrpt_state" but that is not possible here because
* the corresponding DEFER_INTERRUPTS happened in "gtm_trigger" or "gtm_trigger_invoke"
* (in both cases, a different function) so we have an assert there that the previous state was INTRPT_OK_TO_INTERRUPT
* and use that instead of prev_intrpt_state here.
*/
if (forced_unwind)
{ /* Since we are being force-unwound, we don't know the state of things except that it it should be either
* the state we set it to or the ok-to-interrupt state. Assert that and if we are changing the state,
* be sure to run the deferred handler.
*/
assert((INTRPT_IN_TRIGGER_NOMANS_LAND == intrpt_ok_state) || (INTRPT_OK_TO_INTERRUPT == intrpt_ok_state));
ENABLE_INTERRUPTS(intrpt_ok_state, INTRPT_OK_TO_INTERRUPT);
} else
{ /* Normal unwind should be ok with this macro */
ENABLE_INTERRUPTS(INTRPT_IN_TRIGGER_NOMANS_LAND, INTRPT_OK_TO_INTERRUPT);
}
}
/* Routine to eliminate the zlinked trigger code for a given trigger about to be deleted. Operations performed
* differ depending on platform type (shared binary or not).
*/
void gtm_trigger_cleanup(gv_trigger_t *trigdsc)
{
rtn_tabent *mid;
mident *rtnname;
rhdtyp *rtnhdr;
int size;
stack_frame *fp;
intrpt_state_t prev_intrpt_state;
/* TODO: We don't expect the trigger source to exist now gtm_trigger cleans it up ASAP. Remove it after a few releases */
assert (0 == trigdsc->xecute_str.str.len);
/* First thing to do is release trigger source field if it exists */
if (0 < trigdsc->xecute_str.str.len)
{
free(trigdsc->xecute_str.str.addr);
trigdsc->xecute_str.str.len = 0;
trigdsc->xecute_str.str.addr = NULL;
}
/* Next thing to do is find the routine header in the rtn_names list so we can remove it. */
rtnname = &trigdsc->rtn_desc.rt_name;
rtnhdr = trigdsc->rtn_desc.rt_adr;
/* Only one possible version of a trigger routine */
assert(USHBIN_ONLY(NULL) NON_USHBIN_ONLY(rtnhdr) == OLD_RHEAD_ADR(CURRENT_RHEAD_ADR(rtnhdr)));
/* Verify trigger routine we want to remove is not currently active. If it is, we need to assert fail.
* Triggers are not like regular routines since they should only ever be referenced from the stack during a
* transaction. Likewise, we should only ever load the triggers as the first action in that transaction.
*/
# ifdef DEBUG
for (fp = frame_pointer; NULL != fp; fp = SKIP_BASE_FRAME(fp->old_frame_pointer))
assert(fp->rvector != rtnhdr);
# endif
/* Locate the routine in the routine table while all the pieces are available. Then remove from routine table
* after the routine is unlinked.
*/
assertpro(find_rtn_tabent(&mid, rtnname)); /* Routine should be found (sets "mid" with found entry) */
assert(rtnhdr == mid->rt_adr);
/* Free all storage allocated on behalf of this trigger routine. Do this before removing from routine table since
* some of the activities called during unlink look for the routine so it must be found.
*/
DEFER_INTERRUPTS(INTRPT_IN_RTN_CLEANUP, prev_intrpt_state);
zr_unlink_rtn(rtnhdr, TRUE);
/* Remove the routine from the rtn_table */
size = INTCAST((char *)rtn_names_end - (char *)mid);
if (0 < size)
memmove((char *)mid, (char *)(mid + 1), size); /* Remove this routine name from sorted table */
rtn_names_end--;
ENABLE_INTERRUPTS(INTRPT_IN_RTN_CLEANUP, prev_intrpt_state);
}
#endif /* GTM_TRIGGER */
|